diff --git a/README.md b/README.md new file mode 100644 index 0000000..a6a139c --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +Color Anomaly Detection +======================= + +Written as part of a class project on color anomaly detection for search and rescue purposes, this repository contains a number of Matlab implementations of common anomaly detection algorithm from literature on hyperspectral techniques, as well as our own algorithm (PCAG) selected to balance performance and computation time. + +The full technical report on the project, including citations and evaluations of the different anomaly detection algorithms is available online at: + +[Color Outlier Detection for Search and Rescue](http://www.nathanntg.com/writing/color-anomaly-detection.pdf) + + +Implemented hyperspectral algorithms +------------------------------------ + +The following hyperspectral algorithms are implemented in this repository: + +* Global RX (`rxg`) +* Local RX (`rxl`) +* Dual Window-based Eigen Separation Transform (`dwest`) +* Nested Spatial Window-based Target Detection (`nswtd`) +* Multiple Window Nested Spatial Window-based Target Detection (`mwnswtd`) + +Each function above takes a single argument representing the image as a Matlab double matrix with spectral intensities between 0 and 1. + +Novel anomaly detection algorithms +---------------------------------- + +The following new algorithms are implemented in this repository: + +* Principal Component Analysis Gaps (`pcag`) +* Multiple Window Principal Component Analysis Gaps (`mwpcag`) + +Utility functions +----------------- + +A few useful utility functions are implemented in this repository: + +* `im_norm = normalize_image(im)` normalizes an image to be stored in a Matlab double matrix and discarding any transparency channel data. +* `[scene, target] = build_scene(file, num_anomalies, blended)` superimposes random anomalies (from an "anomalies" directory) onto a scene (from a "scenes" directory) in a random position and rotation, and with luminance matching to the surrounding pixels. Returns both the new scene (scene) and a target image (target), representing anomaly positions. +* `[tpr, fpr, th, auc] = roc_anomaly(target, out)` calculates the ROC curve for a particular anomaly detector output (out) based on the target image (target). Shows a plot and returns the true-positive rate (tpr), false-positive rate (fpr), thresholds (th) and area under the curve (auc). + +Analysis scripts +---------------- + +* `run` evaluates the above algorithms over a number of scenes and color spaces in parallel (using `run_script`) +* `analyze` evaluates the output of the run function above across algorithms and colorspaces +* `analyze_environ` evaluates the output of the run function above across scene types (assuming consistently prefixed scene names) + +### Authors + +**L. Nathan Perkins** + +- +- + +**Travis Marshall** diff --git a/RX_global.m b/RX_global.m deleted file mode 100644 index dbfeaac..0000000 --- a/RX_global.m +++ /dev/null @@ -1,32 +0,0 @@ -function img_out = RX_global(Data) -% Perform RX algorithm on global of image - -% Dimensions of block -img_size1=size(Data,1); -img_size2=size(Data,2); -channels=size(Data,3); - -% Separate block into color channels to calculate K matrix -CC = reshape(Data,[],channels); -Kinv = inv(cov(CC)); - -% Create mean color column vector -mu = mean(CC); - -% zero-mean -CCrows = size(CC, 1); -CC = CC - (ones(CCrows, 1) * mu); - -% Preallocate -img_out = zeros(CCrows, 1); - -for i = 1:CCrows - % Locate center pixel and convert to column vector - r = CC(i, :); - - % Run RX detector on center pixel - img_out(i)= r * Kinv * r'; -end - -img_out = reshape(img_out, img_size1, img_size2); - diff --git a/analyze.m b/analyze.m index 90a1af4..232ce52 100644 --- a/analyze.m +++ b/analyze.m @@ -48,7 +48,7 @@ end % calculate AUC - [~, ~, ~, auc] = roc_anomally(target, out); + [~, ~, ~, auc] = roc_anomaly(target, out); title(sprintf('%s with %s color space', algos_nice{j}, color_spaces{i})); print(gcf, sprintf('roc/%d-%s.png', i, algo), '-dpng', '-r300'); diff --git a/analyze_environ.m b/analyze_environ.m index 84a50ff..5b0ef76 100644 --- a/analyze_environ.m +++ b/analyze_environ.m @@ -53,7 +53,7 @@ end % calculate AUC - [~, ~, ~, auc] = roc_anomally(target, out); + [~, ~, ~, auc] = roc_anomaly(target, out); title(sprintf('%s for %s scenes', algos_nice{j}, scene_nice{i})); print(gcf, sprintf('roc/%s-%s.png', scene_nice{i}, algo), '-dpng', '-r300'); diff --git a/pcag_block.m b/pcag_block.m index 70ee4a0..84ea660 100644 --- a/pcag_block.m +++ b/pcag_block.m @@ -1,7 +1,7 @@ function px = pcag_block(block_struct) %PCAG Perform the PCAG algorithm on img. -% size of anomally +% size of anomaly min_size = 4; max_size = min(100, floor(0.5 * prod(block_struct.blockSize))); diff --git a/performance.m b/performance.m index ff90c26..91cf418 100644 --- a/performance.m +++ b/performance.m @@ -1,5 +1,5 @@ % algorithms to performance test -algos = {@RX_global, @rxl, @dwest, @nswtd, @mwnswtd, @pcag, @mwpcag, @pcad, @knna}; +algos = {@rxg, @rxl, @dwest, @nswtd, @mwnswtd, @pcag, @mwpcag, @pcad, @knna}; algos_nice = {'Global RX', 'Local RX', 'DWEST', 'NSWTD', 'MW-NSWTD', 'PCAG', 'MW-PCAG', 'PCAD', 'KNN'}; % scenes to compare diff --git a/roc_anomally.m b/roc_anomaly.m similarity index 80% rename from roc_anomally.m rename to roc_anomaly.m index f3f0b34..1e7fe28 100644 --- a/roc_anomally.m +++ b/roc_anomaly.m @@ -1,6 +1,6 @@ -function [tpr, fpr, th, auc] = roc_anomally(im_target, im_out) -%ROC_ANOMALLY Plot receiver operating characteristic and return values. -% Takes a target image and the output of an anomally detector. +function [tpr, fpr, th, auc] = roc_anomaly(im_target, im_out) +%ROC_ANOMALY Plot receiver operating characteristic and return values. +% Takes a target image and the output of an anomaly detector. % Shows a plot of the ROC curve. % remove color component diff --git a/run_script.m b/run_script.m index b3eef40..e9e318c 100644 --- a/run_script.m +++ b/run_script.m @@ -51,7 +51,7 @@ function run_script(scene_file) % RX GLOBAL fname = sprintf('output/%s-%d-rx.mat', scene_file, j); if ~exist(fname, 'file') - img_rx = RX_global(img); + img_rx = rxg(img); save(fname, 'img_rx'); end diff --git a/rxg.m b/rxg.m new file mode 100644 index 0000000..bf7bd5f --- /dev/null +++ b/rxg.m @@ -0,0 +1,29 @@ +function im_result = rxg(img) +% Perform RX algorithm on global of image + +% Dimensions of block +[height, width, channels] = size(img); + +% Separate block into color channels to calculate K matrix +cc = reshape(img, [], channels); +k_inv = inv(cov(cc)); + +% Create mean color column vector +mu = mean(cc); + +% zero-mean +cc_rows = size(cc, 1); +cc = cc - (ones(cc_rows, 1) * mu); + +% Preallocate +im_result = zeros(cc_rows, 1); + +for i = 1:cc_rows + % Locate center pixel and convert to column vector + r = cc(i, :); + + % Run RX detector on center pixel + im_result(i) = r * k_inv * r'; +end + +im_result = reshape(im_result, height, width);