Skip to content

Commit

Permalink
First Working Draft of Integration Code
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian committed Apr 28, 2020
1 parent e3adf81 commit dcdf0a0
Show file tree
Hide file tree
Showing 13 changed files with 576 additions and 0 deletions.
Binary file added Data/SandingImagesCalibration_Min.mat
Binary file not shown.
Binary file added Data/testImage1.bmp
Binary file not shown.
Binary file added Data/testImage13.bmp
Binary file not shown.
19 changes: 19 additions & 0 deletions Dependency_Analysis.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
%% Run the Toolbox Dependency Analysis
fileList = {};

moduleFilesStruct = dir('Modules');
moduleFiles = arrayfun(@(x) [x.folder '\' x.name],moduleFilesStruct,'UniformOutput',false);

mainFilesStruct = dir;
mainFiles = arrayfun(@(x) x.name, mainFilesStruct, 'UniformOutput',false);

for i = 1:length(moduleFiles)
if(endsWith(moduleFiles{i} ,'.m')); fileList{end+1} = moduleFiles{i}; end;
end

for i = 1:length(mainFiles)
if(endsWith(mainFiles{i} ,'.m')); fileList{end+1} = mainFiles{i}; end;
end

toolBoxDependencies = dependencies.toolboxDependencyAnalysis(fileList);
dependencies.toolboxDependencyAnalysis({'RESClassifier.m'})
326 changes: 326 additions & 0 deletions Modules/Calibration.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
% =========================================================================
% === Calibration Package for Automated Surface Inspection Integration ===
% =========================================================================
% Author: Christian Schirmer (christian.schirmer@uconn.edu)
% =========================================================================

classdef Calibration < handle

properties
% calData stores region bitmasks, along with corresponding pose and
% section identifiers
calData = table( ...
'Size',[0 3],...
'VariableTypes',{'double', 'double', 'cell'},...
'VariableNames',{'PoseID','SectionID','Bitmask'})

% calImages stores calibration images. They are stored as key-value
% pairs <Key, Value> --> <PoseID, Image>
calImages = containers.Map('KeyType','double','ValueType','any');
end

% User-Accessible Calibration Functions
methods (Access = public)
function this = Calibration(varargin)
% CALIBRATION Calibration object constructor. Data will be loaded
% from filename argument, if specified.
if(nargin == 0)
% No input argument: Do nothing, proceed normally.
elseif(nargin ==1)
% 1 input argument: Filename. Load data from this file.
this.loadData(varargin{1});
end
end

function addPose(this, PoseID, image_filepath)
% ADDPOSE: Adds a pose using a calibration image.
% A pose is added with the specified PoseID and calibration image
% at image_filepath. The user is prompted to draw regions around
% the different sections to create the pose-to-section mapping.

% If the pose already exists in the data table, ask the user if
% they would like to replace the pose

% Check to see if the pose already exists in the table
if(any(this.calData.PoseID == PoseID) | this.calImages.isKey(PoseID))
userInput = questdlg(...
['Pose ' num2str(PoseID) ' has already been added. ' ...
'All calibration data associated with this pose ' ...
'will be erased. Would you like to continue?'], ...
['Overwrite Pose' num2str(PoseID) '?'], ...
'Yes','No','No');
switch userInput
case 'Yes'
this.removePose(PoseID);
otherwise
return;
end
end

% Read the calibration image in from the specified filepath
this.calImages(PoseID) = imread(image_filepath);

% Now we go through the region-drawing procedure to create
% bitmasks for all the sections in the calibration image.
sectionsComplete = false;
while(~sectionsComplete)
try
this.addRegion(PoseID);
catch MExc
if(strcmp(MExc.identifier,'Calibration:addRegion:drawROIWindowClosed'))
% This error is thrown when the user closes the UI
% for adding a region before actually drawing the
% region. This indicates the user would like to
% stop adding regions.
sectionsComplete = true;
elseif(strcmp(MExc.identifier,'Calibration:addRegion:sectionInputCancelled'))
% User Cancelled SectionID input. Do nothing.
% Section will not be saved. User will be
% re-prompted to draw section.
else
% Any other errors are not expected and should be
% re-thrown.
rethrow(MExc);
end
end
end
end

function removePose(this, PoseID)
% REMOVEPOSE: Removes all data associated with the specified Pose.
% This includes the calibration image, and all regions associated
% with the pose.

% Remove calibration image
this.calImages.remove(PoseID);
% Remove all regions associated with the specified pose
this.calData = this.calData((this.calData.PoseID~=PoseID),:);
end

function addRegion(this, PoseID)
% ADDREGION: Prompts the user to draw a region in the
% calibration image for the given pose. Prompts user for section
% number. Generates bitmap and appends to calibration data.

% === Retrieve Existing Bitmasks for this PoseID ===
% Get all bitmasks associated with that PoseID
bm_list = this.getBitmasks(PoseID);
% Flatten all the bitmasks on top of each other for display, collect
% information for labeling: section centroid, label text
bm_centroids = {};
bm_labels = {};
for i = 1:height(bm_list)
% Retrieve the i'th bitmask
bitmask_i = logical(bm_list{i,'Bitmask'}{1});
if(i==1)
% First bitmask will not need to be flattened. Assign it as
% totalMask
totalMask = bitmask_i;
else
% Flatten current bitmask into totalMask
totalMask = logical(totalMask) | bitmask_i;
end

% Find Centroid of Region, Save SectionID as Label
% Reference: https://www.mathworks.com/matlabcentral/answers/322369-find-centroid-of-binary-image
[grid_y, grid_x] = ndgrid(1:size(totalMask,1),1:size(totalMask,2));
bm_centroids{i} = mean([grid_x(logical(bitmask_i)),grid_y(logical(bitmask_i))]);

% Store SectionID as label
bm_labels{i} = num2str(bm_list{i,'SectionID'});
end

% === Draw the Figure for the User to Draw a Region on ===

% Have the user define a region by drawing a polygon
roi = images.roi.Polygon;
% Create a figure
thisFig = figure;
% Show the calibration image
curCalImage = imshow(this.calImages(PoseID));
title('Please draw boundary around a section (or close window if complete)');

% If there are existing bitmasks, show them
if(height(bm_list)>0)
hold on;
% Create a green mask layer to display over sections
maskLayer = cat(3,zeros(size(totalMask)),ones(size(totalMask)),zeros(size(totalMask)));
% Draw the already-defined bitmasks on top of the image
maskIm = imshow(maskLayer);
% Label the masks
for i = 1:length(bm_centroids)
text(bm_centroids{i}(1),bm_centroids{i}(2),bm_labels{i});
end
hold off;

% Set the mask layer transparency to 20%
set(maskIm,'AlphaData',totalMask*.2);
end


% Add the ROI drawing tool to the figure so the user can draw
% a region around the next subsection
draw(roi);

% Check to see if window closed
if(~isvalid(roi))
throw(MException('Calibration:addRegion:drawROIWindowClosed','Draw ROI window closed before ROI was drawn'));
end

% Generate the Bitmask
bitmask = createMask(roi, curCalImage);

% Prompt the user for a section number
userInput = inputdlg(...
'SectionID associated with boundary:',...
'Enter SectionID');
if(size(userInput)==[0 0]) % Check to see if user has cancelled input
delete(thisFig);
throw(MException('Calibration:addRegion:sectionInputCancelled','User cancelled section input'));
end
secID = str2num(userInput{1});

% Check to see if the section already exists in the calibration
% data. User can Replace, Merge, or Cancel.
if(any(...
(this.calData.SectionID == secID) & ...
(this.calData.PoseID == PoseID))...
)
userInput = questdlg(...
['Pose ' num2str(PoseID) ' already has a region ' ...
'defined for section ' num2str(secID) '. Would you' ...
' like to replace the existing region, merge in ' ...
' the new selection, or leave the existing region' ...
' as-is?'], ...
['Region Conflict: Pose' num2str(PoseID) ', Section'...
num2str(secID)], ...
'Replace','Merge','Cancel', 'Replace');
switch userInput
case 'Replace'
% Remove the existing bitmask entry for this section,
% replace with the new one.
this.removeCalData(PoseID, secID);
this.appendCalData(PoseID, secID, bitmask);
case 'Merge'
% Merge the existing bitmask with the new one.
old_bm = this.calData(this.calData.PoseID==PoseID & this.calData.SectionID==secID,:).Bitmask{1};
this.calData{this.calData.PoseID==PoseID & this.calData.SectionID==secID,'Bitmask'}= ...
{(old_bm | bitmask)};
case 'Cancel'
% Do nothing.
otherwise
% User input closed. Do nothing.
end
else
% Save the data back to calData table
this.appendCalData(PoseID, secID, bitmask);
end

% Close the figure
delete(thisFig);
end

function save(this, filename)
% SAVE: Saves a .mat file containing the full Calibration object
builtin('save',filename, 'this');
end

function bitmasks = getBitmasks(this, PoseID)
% GETBITMASKS: Get a table of Bitmasks and SectionIDs for a given PoseID
bitmasks = this.calData((this.calData.PoseID==PoseID),{'SectionID', 'Bitmask'});
end

function viewPose(this, PoseID)
% VIEWPOSE: Displays the calibration image for the specified pose,
% as well as all regions that have been associated with the pose.
figure;
imshow(this.getPoseImage(PoseID));
title(['Current Calibration: Pose ' num2str(PoseID)]);

end
end

% Private 'Helper' Functions
methods (Access = private)
function this = appendCalData(this, PoseID, SectionID, bitmask)
% Appends a row to the calData table
this.calData = [this.calData; {PoseID, SectionID, {bitmask}}];
end

function removeCalData(this, PoseID, SectionID)
% Remove a row from the calData table (row(s) corresponding to the
% given PoseID and SectionID
this.calData = this.calData(this.calData.PoseID~=PoseID | this.calData.SectionID ~= SectionID,:);
end

function loadData(this,filename)
% Loads saved data from a .mat file into the Calibration object
fileData = load(filename);
this.calData = fileData.this.calData;
this.calImages = fileData.this.calImages;
end

function compositeImage = getPoseImage(this, PoseID)

% === Retrieve Existing Bitmasks for this PoseID ===
% Get all bitmasks associated with that PoseID
bm_list = this.getBitmasks(PoseID);
% Flatten all the bitmasks on top of each other for display, collect
% information for labeling: section centroid, label text
bm_centroids = {};
bm_labels = {};
for i = 1:height(bm_list)
% Retrieve the i'th bitmask
bitmask_i = logical(bm_list{i,'Bitmask'}{1});
if(i==1)
% First bitmask will not need to be flattened. Assign it as
% totalMask
totalMask = bitmask_i;
else
% Flatten current bitmask into totalMask
totalMask = logical(totalMask) | bitmask_i;
end

% Find Centroid of Region, Save SectionID as Label
% Reference: https://www.mathworks.com/matlabcentral/answers/322369-find-centroid-of-binary-image
[grid_y, grid_x] = ndgrid(1:size(totalMask,1),1:size(totalMask,2));
bm_centroids{i} = mean([grid_x(logical(bitmask_i)),grid_y(logical(bitmask_i))]);

% Store SectionID as label
bm_labels{i} = num2str(bm_list{i,'SectionID'});
end

% Retrieve the calibration image associated with the pose
calImage = this.calImages(PoseID);

% Define the transparency of the bitmasks over the calibration
% image
alpha = .3;

% Create a transparency map: alpha transparency within the
% bitmap region, full transparency everywhere else.
alpha_map = (alpha*totalMask);

% Create a green-only layer
green_image = 255*cat(3,zeros(size(totalMask)),ones(size(totalMask)),zeros(size(totalMask)));

% Convert the bitmask to a green RGB image
% bm_image = uint8(cat(3,zeros(size(totalMask)),totalMask*255,zeros(size(totalMask))));

% Paint the transparent bitmask over the calibration image
% Reference: https://en.wikipedia.org/wiki/Alpha_compositing
compositeImage = uint8((green_image).*alpha_map + double(calImage).*(1-alpha_map));


% Add the section labels to the image.
compositeImage = insertText(compositeImage,...
reshape(cell2mat(bm_centroids),2,length(bm_centroids))',...
bm_labels,...
'AnchorPoint', 'Center',...
'FontSize',min(floor(size(compositeImage,2)/40),200));

end

end

end
27 changes: 27 additions & 0 deletions Modules/Func_Capture_Image.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
function img = Func_Capture_Image()
%FUNC_CAPTURE_IMAGE Implementation of image capture from the camera.
% This function should return an n x m x 3 uint8 image array

test = 1;

if(~test)
% Initialize a connection to the camera
g = gigecam('169.254.90.219','PixelFormat', 'BayerBG8');

% Change the ExposureTime setting (in us)
g.ExposureTimeAbs = 20000;

% Acquire a single image from the camera
img = snapshot(g);

% Clean up by clearing the object.
clear g;
else
% Just return a test image
img = imread('./Data/testImage13.bmp');
end



end

Loading

0 comments on commit dcdf0a0

Please sign in to comment.