Maximally Stable Extremal Regions (MSERs) are found from an image in Matlab using detectMSERFeatures.
Is there any patch or method to get the hierarchical MSER component tree from Matlab?
This tree is anyways being generated when Matlab calculates the regions - it returns only the most "stable" component from each region's tree. Since this tree is already present, I am searching for ways to expose this to user code from the Matlab libraries, which keeps this part hidden and provides only the final "maximally stable" regions.
Anything would be acceptable - modifications of the Matlab inbuilt code, patches, hacks whatever. (I realize OpenCV has such a patch, however I am trying to avoid porting to OpenCV as most of the other procedures are written in Matlab).
EDIT: (from the original hierarchical MSER paper)
Detected MSERs(left), MSER Tree(right)
"Hierarchical MSER component tree" is a confusing phrase, because (1) the component tree is already hierarchical (2) if you want the whole tree, then you don't want only the Maximally Stable Extremal Regions (MSER), but instead you want all the extremal regions, and (3) extremal regions and components are the same thing in this context.
So let's say you want the extremal region tree. As noted in comments, you can't have exactly the one MATLAB uses because detectMSERFeatures.m calls a mex function that we don't have source code for (though, based on its inputs and name, it is likely very similar to the openCV MSER function). But you can still compute your own extremal region tree. Basically what this code does is finds connected components in the image at various levels of thresholding. Those CCs are the extremal regions. The trickiest part of the code is recording the parent relations. This should get you started:
% input image, included with MATLAB
x = imread('rice.png');
pixelList = {};
parents = [];
oldERsLabeled = zeros(size(x));
oldPixelList = {};
regionLabelOffset = 0;
for i = 255:-10:1 % the stride here is important, smaller will be slower and give more regions
newERs = bwlabel(x > i);
newERsLabeled = zeros(size(newERs));
newERsLabeled(newERs > 0) = newERs(newERs > 0) + regionLabelOffset;
regionLabelOffset = max(newERsLabeled(:));
% update the list of regions
props = regionprops(newERs, 'pixelList');
newPixelList = {props(:).PixelList};
pixelList = [pixelList newPixelList];
% figure out parents
newParents = cellfun(#(c)(newERsLabeled( sub2ind(size(x), c(1,2), c(1,1)))), oldPixelList);
parents = [parents; newParents'];
oldPixelList = newPixelList;
oldERsLabeled = newERsLabeled;
end
parents(end+1 : length(pixelList)) = -1; % top level regions have no parents
pixelListInt = cellfun(#int32, pixelList, 'UniformOutput', false);
regions = MSERRegions(pixelListInt');
% plot the first 300 regions
figure
imshow(x)
hold on
plot(regions(1:300), 'showEllipses', false, 'showPixelList', true);
% show all parents of a region ("close all" command might be useful after)
curRegion = 102;
while curRegion ~= -1
figure
imshow(x)
hold on
plot(regions(curRegion), 'showEllipses', false, 'showPixelList', true);
curRegion = parents(curRegion);
end
Related
When designing a CNN for 1D time series signal classification in MATLAB i get the error that the 2dconvolutional layer does not take sequences as input. From my understanding it is perfectly possible to convolve of an "array" with a 3x1 filter. To resolve this issue MATLAB suggests to use a "sequence folding layer". What would be the function of such a sequence folding layer and how would the architecture need to be changed?
I get the following error message:
What would be the function of such a sequence folding layer and how would the architecture need to be changed?
Simply speaking, you're parsing in a sequence (or as you called it - "an array") of images whereas you need to convert them into a batch of images before performing any convolutional operations.
Documentation about sequenceFoldingLayer:
A sequence folding layer converts a batch of image sequences to a batch of images. Use a sequence folding layer to perform convolution operations on time steps of image sequences independently.
Regarding the usage of a sequenceFoldingLayer (once again suggested to check out the documentation):
To use a sequence folding layer, you must connect the miniBatchSize output to the miniBatchSize input of the corresponding sequence unfolding layer. For an example, see Create Network for Video Classification.
On said website are also lots of examples on how to create a sequence folding layer - e.g. one with the name of "fold1":
layer = sequenceFoldingLayer('Name','fold1')
... as well as examples on how to properly implement it within your project.
What would be the function of such a sequence folding layer and how would the architecture need to be changed?
Using sequenceFolding/sequenceUnFoldingLayer works as follows experimentally, the results in the red box in the figure are consistent.
%% Testing the sequenceFoldingLayer/sequenceUnfoldingLayer internal flow
% The difference between using the sequence layer and not using the sequenceFolding/sequenceUnFoldingLayer to extract features
% Common parameters used below
%
% 测试sequenceFoldingLayer/sequenceUnfoldingLayer内部具体操作流程
% 用和不用sequence layer的区别,使用sequenceFolding/sequenceUnFoldingLayer提取出特征结果比较
% 下面使用的共有参数
inputSize = [28 28 1];
filterSize = 5;
numFilters = 20;
numHiddenUnits = 200;
numClasses = 10;
inputData = rand(28,28,1,30);% h*w*c*n
convL = convolution2dLayer(filterSize,numFilters,'Name','conv',...
'Weights',ones(filterSize,filterSize,1,numFilters),...
'Bias',ones(1,1,numFilters));
bnL= batchNormalizationLayer('Name','bn',...
'TrainedVariance',ones(1,1,numFilters),...
'TrainedMean',zeros(1,1,numFilters),...
'Offset',zeros(1,1,numFilters),...
'Scale',ones(1,1,numFilters));
%% first stage, use sequence layer
layers = [ ...
sequenceInputLayer(inputSize,'Name','input')
sequenceFoldingLayer('Name','fold')
convL
bnL
reluLayer('Name','relu')
sequenceUnfoldingLayer('Name','unfold')
flattenLayer('Name','flatten')
lstmLayer(numHiddenUnits,'OutputMode','last','Name','lstm',...
'InputWeights',ones(800,11520),...
'RecurrentWeights',ones(800,200),...
'Bias',ones(800,1));
fullyConnectedLayer(numClasses, 'Name','fc',...
'Weights',ones(numClasses,200),...
'Bias',ones(numClasses,1));
softmaxLayer('Name','softmax')
classificationLayer('Name','classification')];
lgraph = layerGraph(layers);
lgraph = connectLayers(lgraph,'fold/miniBatchSize','unfold/miniBatchSize');
assembleNet = assembleNetwork(lgraph);
%% second stage: not use sequence layer
% The sequenceFoldingLayer is essentially a per-frame graph acquisition feature as follows
layers2 = [ ...
imageInputLayer(inputSize,'Name','input','Normalization','none')
convL
bnL
reluLayer('Name','relu')
fullyConnectedLayer(numClasses, 'Name','fc',...
'Weights',ones(numClasses,11520),...
'Bias',ones(numClasses,1));
softmaxLayer('Name','softmax')
classificationLayer('Name','classification')];
lgraph2 = layerGraph(layers2);
assembleNet2 = assembleNetwork(lgraph2);
%% visualize
analyzeNetwork(lgraph)
analyzeNetwork(lgraph2)
%% comparison
out1 = activations(assembleNet,inputData,'unfold','OutputAs','channels');
out2 = activations(assembleNet2,inputData,'relu','OutputAs','channels');
t1 = out1{1};
t2 = out2;
%% result is same
t1(1:10)
t2(1:10)
Apologies for the long post but this takes a bit to explain. I'm trying to make a script that finds the longest linear portion of a plot. Sample data is in a csv file here, it is stress and strain data for calculating the shear modulus of 3D printed samples. The code I have so far is the following:
x_data = [];
y_data = [];
x_data = Data(:,1);
y_data = Data(:,2);
plot(x_data,y_data);
grid on;
answer1 = questdlg('Would you like to load last attempt''s numbers?');
switch answer1
case 'Yes'
[sim_slopes,reg_data] = regr_and_longest_part(new_x_data,new_y_data,str2num(answer2{3}),str2num(answer2{2}),K);
case 'No'
disp('Take a look at the plot, find a range estimate, and press any button to continue');
pause;
prompt = {'Eliminate values ABOVE this x-value:','Eliminate values BELOW this x-value:','Size of divisions on x-axis:','Factor for similarity of slopes:'};
dlg_title = 'Point elimination';
num_lines = 1;
defaultans = {'0','0','0','0.1'};
if isempty(answer2) < 1
defaultans = {answer2{1},answer2{2},answer2{3},answer2{4}};
end
answer2 = inputdlg(prompt,dlg_title,num_lines,defaultans);
uv_of_x_range = str2num(answer2{1});
lv_of_x_range = str2num(answer2{2});
x_div_size = str2num(answer2{3});
K = str2num(answer2{4});
close all;
iB = find(x_data > str2num(answer2{1}),1,'first');
iS = find(x_data > str2num(answer2{2}),1,'first');
new_x_data = x_data(iS:iB);
new_y_data = y_data(iS:iB);
[sim_slopes, reg_data] = regr_and_longest_part(new_x_data,new_y_data,str2num(answer2{3}),str2num(answer2{2}),K);
end
[longest_section0, Midx]= max(sim_slopes(:,4)-sim_slopes(:,3));
longest_section=1+longest_section0;
long_sec_x_data_start = x_div_size*(sim_slopes(Midx,3)-1)+lv_of_x_range;
long_sec_x_data_end = x_div_size*(sim_slopes(Midx,4)-1)+lv_of_x_range;
long_sec_x_data_start_idx=find(new_x_data >= long_sec_x_data_start,1,'first');
long_sec_x_data_end_idx=find(new_x_data >= long_sec_x_data_end,1,'first');
long_sec_x_data = new_x_data(long_sec_x_data_start_idx:long_sec_x_data_end_idx);
long_sec_y_data = new_y_data(long_sec_x_data_start_idx:long_sec_x_data_end_idx);
[b_long_sec, longes_section_reg_data] = robustfit(long_sec_x_data,long_sec_y_data);
plot(long_sec_x_data,b_long_sec(1)+b_long_sec(2)*long_sec_x_data,'LineWidth',3,'LineStyle',':','Color','k');
function [sim_slopes,reg_data] = regr_and_longest_part(x_points,y_points,x_div,lv,K)
reg_data = cell(1,3);
scatter(x_points,y_points,'.');
grid on;
hold on;
uv = lv+x_div;
ii=0;
while lv <= x_points(end)
if uv > x_points(end)
uv = x_points(end);
end
ii=ii+1;
indices = find(x_points>lv & x_points<uv);
temp_x_points = x_points((indices));
temp_y_points = y_points((indices));
if length(temp_x_points) <= 2
break;
end
[b,stats] = robustfit(temp_x_points,temp_y_points);
reg_data{ii,1} = b(1);
reg_data{ii,2} = b(2);
reg_data{ii,3} = length(indices);
plot(temp_x_points,b(1)+b(2)*temp_x_points,'LineWidth',2);
lv = lv+x_div;
uv = lv+x_div;
end
sim_slopes = NaN(length(reg_data),4);
sim_slopes(1,:) = [reg_data{1,1},0,1,1];
idx=1;
for ii=2:length(reg_data)
coff =sim_slopes(idx,1);
if abs(reg_data{ii,1}-coff) <= K*coff
C=zeros(ii-sim_slopes(idx,3)+1,1);
for kk=sim_slopes(idx,3):ii
C(kk)=reg_data{kk,1};
end
sim_slopes(idx,1)=mean(C);
sim_slopes(idx,2)=std(C);
sim_slopes(idx,4)=ii;
else
idx = idx + 1;
sim_slopes(idx,1)=reg_data{ii,1};
sim_slopes(idx,2)=0;
sim_slopes(idx,3)=ii;
sim_slopes(idx,4)=ii;
end
end
end
Apologies for the code not being well optimized, I'm still relatively new to MATLAB. I did not use derivatives because my data is relatively noisy and derivation might have made it worse.
I've managed to get the get the code to find the longest straight part of the plot by splitting the data up into sections called x_div_size then performing a robustfit on each section, the results of which are written into reg_data. The code then runs through reg_data and finds which lines have the most similar slopes, determined by the K factor, by calculating the average of the slopes in a section of the plot and makes a note of it in sim_slopes. It then finds the longest interval with max(sim_slopes(:,4)-sim_slopes(:,3)) and performs a regression on it to give the final answer.
The problem is that it will only consider the first straight portion that it comes across. When the data is plotted, it has a few parts where it seems straightest:
As an example, when I run the script with answer2 = {'0.2','0','0.0038','0.3'} I get the following, where the black line is the straightest part found by the code:
I have the following questions:
It's clear that from about x = 0.04 to x = 0.2 there is a long straight part and I'm not sure why the script is not finding it. Playing around with different values the script always seems to pick the first longest straight part, ignoring subsequent ones.
MATLAB complains that Warning: Iteration limit reached. because there are more than 50 regressions to perform. Is there a way to bypass this limit on robustfit?
When generating sim_slopes there might be section of the plot whose slope is too different from the average of the previous slopes so it gets marked as the end of a long section. But that section sometimes is sandwiched between several other sections on either side which instead have similar slopes. How would it be possible to tell the script to ignore one wayward section and to continue as if it falls within the tolerance allowed by the K value?
Take a look at the Douglas-Peucker algorithm. If you think of your (x,y) values as the vertices of an (open) polygon, this algorithm will simplify it for you, such that the largest distance from the simplified polygon to the original is smaller than some threshold you can choose. The simplified polygon will be the set of straight lines. Find the two vertices that are furthest apart, and you're done.
MATLAB has an implementation in the Mapping Toolbox called reducem. You might also find an implementation on the File Exchange (but be careful, there is also really bad code on there). Or, you can roll your own, it's quite a simple algorithm.
You can also try using the ischange function to detect changes in the intercept and slope of the data, and then extract the longest portion from that.
Using the sample data you provided, here is what I see from a basic attempt:
>> T = readtable('Data.csv');
>> T = rmmissing(T); % Remove rows with NaN
>> T = groupsummary(T,'Var1','mean'); % Average duplicate timestamps
>> [tf,slopes,intercepts] = ischange(T.mean_Var2, 'linear', 'SamplePoints', T.Var1); % find changes
>> plot(T.Var1, T.mean_Var2, T.Var1, slopes.*T.Var1 + intercepts)
which generates the plot
You should be able to extract the longest segment based on the indices given by find(tf).
You can also tune the parameters of ischange to get fewer or more segments. Adding the name-value pair 'MaxNumChanges' with a value of 4 or 5 produces more linear segments with a tighter fit to the curve, for example, which effectively removes the kink in the plot that you see.
I am trying to implement code for the light speed labeling technique described in this article (I cannot use the Image Processing Toolbox): https://pdfs.semanticscholar.org/ef31/7c257603004d818ca1e2a2aa67d36d40147e.pdf (see section 2, page 7).
Here is my Matlab code for LSL equivalence construction (algorithm 14, step 2).
function [EQ,ERAi,nea] = LSL_equivalence(EQ,ERim1,RLCi,ERAim1,ERAi,NERi,nea,lImg)
% LSL_EQUIVALENCE build the associative table between er and ea
% GOAL: to create a Look Up Table to be applied to ERi to create EAi.
for er = 1:2:NERi % check segments one by one
% read the boundaries of each segment to obtain de relative labels of every agacent segment in the prev line
j0 = RLCi(er);
j1 = RLCi(er+1);
er0 = ERim1(j0+1); % label of the first segment
er1 = ERim1(j1+1); % the label of the last segment
% check label parity: segments are odd, background is even
% bitand([1 2 3 4 5],1) == [1 0 1 0 1]
if bitand(er0,1) == 0 % if er0 is even
er0 = er0 + 1;
end
if bitand(er1,1) == 0 % if er1 is even
er1 = er1 -1;
end
if er1 >= er0 % if there is an adjacency
ea = ERAim1(er0+1); % absolute label of the first segment
a = EQ(ea+1); % a is the ancestor (smallest label of the equivalence class)
for erk = (er0+2):2:er1
eak = ERAim1(erk+1);
ak = EQ(eak+1);
% min extraction and propagation
if a < ak
EQ(eak+1) = a;
else
a = ak;
EQ(ea+1) = a;
ea = eak;
end
end
ERAi(er+1) = a; % the global min of all ak ancestors
else % if there are no adjacent labels make a new label
nea = nea + 1;
ERAi(er+1) = nea;
end
end
end
I am having some trouble with indexes, as the pseudo code described in the article has indexes starting with 0 and Matlab works with 1. I have already found some C++ code in this Stack Overflow post Implementing LSL for Connected Component Labeling/Blob Extraction (I applied suggested changes) and also in this git repo https://github.com/prittt/YACCLAB/blob/master/include/labeling_lacassagne_2016_code.inc. I fail to see the differences.
Also, I'm having some trouble understanding what an equivalence class is (which is what goes in matrix EQ). Thanks ahead of time!
I realize this is coming a bit late, but I just put up a piece of code that is similar to what the light speed labeling algorithm does. I'll give a brief description of how I solved the index problem.
The first step of LSL is take each column of pixels and finds the start and stop positions of consecutively labeled pixels. You can do that in Matlab like this:
I = imread('text.png');
[rows,cols] = find(xor(I(2:end,:),I(1:end-1,:)));
What this gives you is the row and column of the start and stop position of each run of pixels in a column, except its non-inclusive indexing. By non-inclusive indexing I mean the indices of pixels runs from I(r(2*n-1),c(2*n-1)) to I(r(2*n)-1,c(2*n)) for each pixel run (n). You should note that the paper operates along rows where the above code operates along columns, but the same principle applies. You should also note that the above code does not cover the circumstance of labeled pixels on the edge of the image.
If you want to see a complete implementation, I posted my code on the Matlab File Exchange. I don't claim that it copies LSL exactly, but it works on many of the same principles.
https://www.mathworks.com/matlabcentral/fileexchange/70323-ftllabel?s_tid=prof_contriblnk
I would like to compare which are the 5 most similar images to an input image.
To do this I thought to use the SIFT (VLFeat library) and compare the respective descriptors.
So I use the vl_ubcmatch (doc here) method to calculate the similarity measurement between the images.
This is the code:
path_dir = './img/';
imgs = dir(path_dir);
imgs = imgs(3 : end);
numImgs = size(imgs);
numImgs = numImgs(1);
path1 = './img/car01.jpg';
Ia = imread(path1);
Ia = single(rgb2gray(Ia));
[fa, da] = vl_sift(Ia);
results = struct;
m = 0;
j = 1; % indice dell'img (del for)
for img = imgs'
path = strcat(path_dir, img.name);
if(strcmp(path1, path) == 0)
Ib = imread(path);
Ib = single(rgb2gray(Ib));
[fb, db] = vl_sift(Ib);
[matches, scores] = vl_ubcmatch(da, db);
s = sum(scores);
[r, c] = size(scores);
m = s ./ c;
results(j).measure = m;
results(j).img = path;
j = j + 1;
end
end
As you can see from the code, I thought I would use the mean as a measure of similarity but the results I get are not satisfactory (for example, it tells me that the input image of a cup is more similar to a tree than another cup).
According to you, is it better to have more equal descriptors but with low similar or less similar descriptors but with greater similarity?
I have 50 images of 5 different categories (cups, trees, people, tables and cars) and, given an image as input, the program will return the 5 most similar images to it and preferably belonging to the same category.
What measurement can I use instead of the mean to get a more precise classification?
Thanks!
According to your code you measure the similarity between image (Ia) and all other images (Ib). Therefore you compare the SIFT descriptors of Ia with those of all Ib's - which gives you a list of feature matches for each image pair (matches) and the Euclidean distance of each feature pair (scores).
Now using the mean of all scores of an image pair as a measure of similarity is not a very robust approach because an image pair with only one feature match could (by chance) lead to a better "similarity" than an image pair with many features - which I guess is an unrealistic solution for your task.
Concerning your question it is always better to have meaningful/robust descriptors, even if there are only a few (of course the more the better!), than having a lot of meaningless descriptors.
Proposal: why don't you just count the number of inliers (= number of feature matches for each image pair, numel(matches))?
With this it should give more inliers between images of the same object than different objects, so taking those pairs which have the 5 most inliers should be the most similar ones.
If you just want to distinguish a cup from a tree it should work. If your classification task is getting more difficult and you need to distinguish different types of trees, SIFT is not the best algorithm to use. A learning approach will give better results... but depends on your task.
I found here How are HoG features represented graphically? code to visualize HOG features; it is done by 2 files in http://www.cs.berkeley.edu/~rbg/latent/index.html, visualizeHOG.m and
HOGpicture.m that is
(below code is released under an MIT license)
function im = HOGpicture(w, bs)
% Make picture of positive HOG weights.
% im = HOGpicture(w, bs)
% construct a "glyph" for each orientation
bim1 = zeros(bs, bs);
bim1(:,round(bs/2):round(bs/2)+1) = 1;
bim = zeros([size(bim1) 9]);
bim(:,:,1) = bim1;
for i = 2:9,
bim(:,:,i) = imrotate(bim1, -(i-1)*20, 'crop');
end
% make pictures of positive weights bs adding up weighted glyphs
s = size(w);
w(w < 0) = 0;
im = zeros(bs*s(1), bs*s(2));
for i = 1:s(1),
iis = (i-1)*bs+1:i*bs;
for j = 1:s(2),
jjs = (j-1)*bs+1:j*bs;
for k = 1:9,
im(iis,jjs) = im(iis,jjs) + bim(:,:,k) * w(i,j,k);
end
end
end
I don't undestand what is the bs parameter and what means..anycan can help me?
If you looking for visualizing HOG, you can have a look here, http://web.mit.edu/vondrick/ihog/#code
It was recently published in iccv 2013
If you want to visualize HOG features, then use VLFeat (there is a option called render which allows you to do this). The ICCV paper mentioned in the answer below reconstructs HOG features into an image. It tries to show you, "what computers would have seen"? Both are different, you may want to try both.
bs stands for bin size. Usually 8x8 (therefore, bs=8) is used but you should know what was the value of the bin size because that is a necessary parameter in computing HOG itself.
The extractHOGFeatures function in the Computer Vision System Toolbox for MATLAB optionally returns a visualization object that lets you visualize the features.