Skip to content
This repository has been archived by the owner on Nov 16, 2023. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
matekd committed Jul 26, 2018
2 parents 6d9564e + a685831 commit bd6f729
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 40 deletions.
62 changes: 33 additions & 29 deletions src/AccuracyAssessment/validateThermal.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
falseNegatives = 0
hotSpotDistances = []
registrationDistances = []
truePositives = 0
classificationTrue = 0
classificationFalse = 0
expectedTruePositives = 0
Expand Down Expand Up @@ -77,7 +78,7 @@ def getSortKey(item):
for key in resDict:
# if photo not in expected dictionary that means there were no animals in the entire photo so throw FalsePositive
if key not in expectedDict:
falsePositives+=1
falsePositives+=len(resDict[key])
else:
resRows = resDict.get(key)
expectedRows = expectedDict.get(key)
Expand Down Expand Up @@ -107,50 +108,53 @@ def getSortKey(item):
if (len(expectedRowsData) > 1):
expectedRowsData.sort(key=getSortKey)

for m in range(minLength):
resRow = resRowsData[m]
expectedRow = expectedRowsData[m]

# 1 is expected values; 2 is ouput values. Both are location on the image
x1 = int(expectedRow[5])
y1 = int(expectedRow[6])
x2 = int(resRow[5])
y2 = int(resRow[6])
dist = math.hypot(x2 - x1, y2 - y1)

hotSpotDistances.append(dist)
# The average size of seal on thermal image is about 2 by 5 pixels. Assume hot spot detedted within
# the size of a seal is a success detection.
if (dist < locationOffsetTolerance):
hotspotLocationTrue+=1
for e in expectedRowsData:
minDist = 99999999
for r in resRowsData:
if r[1] != 'MatchesGroundTruth':
x1 = int(expectedRow[5])
y1 = int(expectedRow[6])
x2 = int(resRow[5])
y2 = int(resRow[6])
dist = math.hypot(x2 - x1, y2 - y1)
if dist < minDist:
minDist = dist
minResRow = r

# classification accuracy
if (expectedRow[12] == resRow[12]):
classificationTrue += 1
# Was there a detected hotspot within tolerance of this ground truth hotspot?
if minDist < locationOffsetTolerance:
truePositives += 1
minResRow[1] = 'MatchesGroundTruth' # Hackily reuse the "timestamp" field to indicate that we have a match
if minResRow[13] == e[13]:
classificationTrue += 1
else
classificationFalse += 1

for r in resRowsData:
if r[1] != 'MatchesGroundTruth':
falsePositives += 1
else:
classificationFalse += 1
if r[13] ==


# Print results on screen
print("Accuracy assessment for thermal image classification")
print("Number of hot spots found: " + str(len(resList)-1))
print("Percent of hot spots found: " + str((classificationTrue + classificationFalse) / expectedTruePositives*100) + " percent.")
print("Percent of hot spots found: " + str(truePositives / expectedTruePositives*100) + " percent.")
print("There were " + str(falsePositives) + " false positives.")
print("There were " + str(falseNegatives) + " false negatives.")
if ((classificationTrue + classificationFalse) > 0):
if ((truePositives) > 0):
print("There was a classification accuracy of " + str((classificationTrue / (classificationTrue + classificationFalse)) * 100) + " percent.")
if (len(hotSpotDistances) > 0):
print("There was an average hot spot distance of " + str(sum(hotSpotDistances) / len(hotSpotDistances)))
print("There was an hot spot location detection accuracy of " + str(hotspotLocationTrue / len(hotSpotDistances)*100) + " percent.")

# Print result to a file
with open('assessmentResults.txt', 'w') as f:
print("Accuracy assessment for thermal image classification", file=f)
print("Number of hot spots found: " + str(len(resList)-1), file=f)
print("Percent of hot spots found: " + str((classificationTrue + classificationFalse) / expectedTruePositives*100) + " percent.", file=f)
print("Percent of hot spots found: " + str((truePositives) / expectedTruePositives*100) + " percent.", file=f)
print("There were " + str(falsePositives) + " false positives.", file=f)
print("There were " + str(falseNegatives) + " false negatives.", file=f)
if ((classificationTrue + classificationFalse) > 0):
print("There was a classification accuracy of " + str((classificationTrue / (classificationTrue + classificationFalse)) * 100) + " percent.", file=f)
if ((truePositives) > 0):
print("There was a classification accuracy of " + str((truePositives / (truePositives)) * 100) + " percent.", file=f)
if (len(hotSpotDistances) > 0):
print("There was an average hot spot distance of " + str(sum(hotSpotDistances) / len(hotSpotDistances)), file=f)
print("There was an hot spot location detection accuracy of " + str(hotspotLocationTrue / len(hotSpotDistances)*100) + " percent.", file=f)
Expand Down
42 changes: 41 additions & 1 deletion src/ir-hotspot-rfc/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,43 @@
## Hotspot Classifier based on Random Forest Classifier

Will be filled in in next commit
This folder contains the code for training and applying a random forest classifier in sci-kit learn to the arcticseal data. The classifier uses Sci-Kit Learn's classifier class so they can easily be switched out to test other classifiers. We are also using PCA to project the raw data onto "eigen-seals" like the following table:
|Eigen Seal 1 | Eigen Seal 2|
|------------ | ------------|
|![alt text](./images/eigenseal1.png "Eigen-Seal 1") | ![alt text](./images/eigenseal2.png "Eigen-Seal 2")|

## Classifying
The classification script supports two modes for processing images: command line and as a library import to another script.

#### Launch Classifier from the Command line
The script requires three flags from the command line with an optional fourth:
1. `--datadir` the path to the directory where the thermal images are stored
1. `--datafile` the path to the csv file that you want to process
1. `--modelfile` the path to the pickled version of the classifier (output by hotspot_training.py)
1. `--outfile` (optional) the path where the csv with classification information should be written. Defaults to `./output.csv`

The classified CSV file will have the column `hotspot_type` filled in with either "Anamoly" or "Animal" depending on the classification. It will also fill in the column `ir_confidence` that contains the confidence that the classifier has in its decision.

An example of running the script is shown below:
```bash
python hotspot_classifier.py --datadir ./ArcticSealsData01_Thermal_N/ --datafile ../arcticseals/data/test.csv --modelfile pca_rfc_model_20180725_154906.p
```

#### Use Classifier as a Import
The main function for classifying is called `classify_data` and takes the same input values that are passed from the command line as arguments. The function definition and doc string are shown here for convenience:
```python
def classify_data(data_file, data_directory, model_file, output_file):
"""Data loading, classifying and output logic. For compatibility with library inputs
Inputs:
data_file: string, path to the input csv file
data_directory: string, path to the thermal images
model_file: string, path to the classifier model
output_file: string, path to the output csv file
"""
```

## File Description
* hotspot_classifier.py: Function for applying the model to a new set of data
* hotspot_training.py: Script for training the model
* pca_rfc_model_20180725_154906.p: Pre-trained model on arcticseals/data/train.csv
* requirements.txt: pip compatible file listing the required dependencies. Install using `pip install requirements.txt`
* README.md: this file
Binary file added src/ir-hotspot-rfc/images/eigenseal1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/ir-hotspot-rfc/images/eigenseal2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/viewer/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ angular.module('myApp', [
'myApp.view1',
'myApp.view2',
'myApp.view3',
'myApp.view4',
'myApp.version'
]).
config(['$locationProvider', '$routeProvider', function($locationProvider, $routeProvider) {
Expand Down
4 changes: 3 additions & 1 deletion src/viewer/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>My AngularJS App</title>
<title>Find Seal in Artic with Deep Learning</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="bower_components/html5-boilerplate/dist/css/normalize.css">
Expand All @@ -24,6 +24,7 @@
<li><a href="#!/view1">Color</a></li>
<li><a href="#!/view2">Thermal</a></li>
<li><a href="#!/view3">Area of Interest</a></li>
<li><a href="#!/view4">Aggregate</a></li>
</ul>

<div ng-view></div>
Expand All @@ -34,6 +35,7 @@
<script src="view1/view1.js"></script>
<script src="view2/view2.js"></script>
<script src="view3/view3.js"></script>
<script src="view4/view4.js"></script>
<script src="components/version/version.js"></script>
<script src="components/version/version-directive.js"></script>
<script src="components/version/interpolate-filter.js"></script>
Expand Down
7 changes: 4 additions & 3 deletions src/viewer/app/view3/view3.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

var app = angular.module('myApp.view3', ['ngRoute'])
var artic;
var artic, items;
var getName = function(articI) {
var names = articI.filt_color.split(".");
var name = names[0] + "." + names[1] + "_HOTSPOT_" + articI.hotspot_id + ".JPG";
Expand All @@ -22,7 +22,7 @@ app.controller('View3Ctrl', function($scope) {
$.getJSON( "/data/training.json", function( data ) {
if (data.Artic !== 'undefined') {
artic = data.Artic;
var len = artic.length;
var len = artic.length / 3;
for(var i = 0; i < len; i++) {
var name = getName(artic[i]);
$scope.items.push({"idx": i, "file": "crop-img//" + name});
Expand All @@ -34,13 +34,14 @@ app.controller('View3Ctrl', function($scope) {
$scope.filterMe = function( hotspot_type ) {
if (artic !== 'undefined') {
$scope.items = [];
var len = artic.length;
var len = artic.length / 3;
for(var i = 0; i < len; i++) {
var name = getName(artic[i]);
if (artic[i].hotspot_type === hotspot_type) {
$scope.items.push({"idx": i, "file": "crop-img//" + name});
}
}
items = $scope.items;
$scope.$apply();
}
};
Expand Down
14 changes: 14 additions & 0 deletions src/viewer/app/view4/view4.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Filter:
</button>
<div class="dropdown-menu">
<a class="dropdown-item" ng-click="filterMe('Anomaly')">Anomaly</a>
<a class="dropdown-item" ng-click="filterMe('Animal')">Animal</a>
</div>

<div class="row">
<div ng-repeat="item in items">
<img src="{{item.color}}"></img>
<img src="{{item.thermal}}"></img><br/>
</div>
</div>
50 changes: 50 additions & 0 deletions src/viewer/app/view4/view4.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict';

var app = angular.module('myApp.view4', ['ngRoute'])
var artic, items;
var getName = function(articI) {
var names = articI.filt_color.split(".");
var name = names[0] + "." + names[1] + "_HOTSPOT_" + articI.hotspot_id + ".JPG";
return name;
};

app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/view4', {
templateUrl: 'view4/view4.html',
controller: 'View4Ctrl'
});
}])

app.controller('View4Ctrl', function($scope) {
$scope.items = [];

var json = null;

$.getJSON( "/data/training.json", function( data ) {
if (data.Artic !== 'undefined') {
var artic = data.Artic;
var len = artic.length / 2;
for(var i = 0; i < len; i++) {
var articI = artic[i];
$scope.items.push({"idx": i, "color": "thumb-img//" + articI.filt_color, "thermal" : "thumb-thermal//" + articI.filt_thermal8});
}
$scope.$apply();
}
});

$scope.filterMe = function( hotspot_type ) {
if (artic !== 'undefined') {
$scope.items = [];
var len = artic.length / 3;
for(var i = 0; i < len; i++) {
var name = getName(artic[i]);
if (artic[i].hotspot_type === hotspot_type) {
$scope.items.push({"idx": i, "file": "crop-img//" + name});
}
}
items = $scope.items;
$scope.$apply();
}
};

});
16 changes: 13 additions & 3 deletions src/viewer/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ const app = express();
const router = express.Router();
const $ = jQuery = require('./jquery.js');
var csv = require('./jquery.csv.js');
var exec = require('child_process').exec;
const fs = require('fs');

const port = 3000;

app.use('/', express.static('app'));
Expand All @@ -19,9 +22,16 @@ app.use('/thumb-thermal', express.static('E:\\noaa\\thumb-thermal'));
app.use('/crop-img', express.static('E:\\noaa\\crop-img'));

// using router.get() to prefix our path
// url: http://localhost:3000/api/
app.get('/api', (request, response) => {
response.json({message: 'Hello, welcome to my server'});
// url: http://localhost:3000/api/CHESS_FL1_C_160408_000830.513_COLOR-8-BIT_HOTSPOT_17486.JPG
app.get('/api/:id', (request, response) => {
var filename = 'E:\\noaa\\crop-img\\' + request.params.id;
fs.copyFileSync(filename, 'D:\\github\\seals\\src\\viewer\\util\\cnn\\cur\\cur.jpg');
console.log(request.params.id);
exec('python D:\\github\\seals\\src\\viewer\\util\\cnn\\SealCurrent.py', {cwd:'D:\\github\\seals\\src\\viewer\\util\\cnn'}, function callback(error, stdout, stderr){
var lines = stdout.split('\n');
//console.log(lines[4]);
response.json({message: lines[4]});
});
});

// set the server to listen on port 3000
Expand Down
2 changes: 1 addition & 1 deletion src/viewer/util/Convert CSV to JSON.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.5"
"version": "3.5.5"
}
},
"nbformat": 4,
Expand Down
2 changes: 1 addition & 1 deletion src/viewer/util/Crop Images.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.5"
"version": "3.5.5"
}
},
"nbformat": 4,
Expand Down
3 changes: 2 additions & 1 deletion src/viewer/util/conv16.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
for currentFile in inputPath.glob("*.png"):
currentFilename = str(currentFile.name)
inputFile = os.path.join(inputDir, currentFilename)
img = cv2.imread(inputFile, -cv2.IMREAD_ANYDEPTH).astype(np.uint8)
#img = cv2.imread(inputFile, -cv2.IMREAD_ANYDEPTH).astype(np.uint8)
img = cv2.imread(inputFile, cv2.IMREAD_LOAD_GDAL).astype(np.uint8)
outputFile = os.path.join(outputDir, currentFilename)
cv2.imwrite(outputFile, img)

0 comments on commit bd6f729

Please sign in to comment.