How to detect peaks on gray background with Matlab FastPeakFind? - matlab

I am testing the validity of the FileExchange project FindPeaksFast with different linewidths and backgrounds.
Test 1 is successful and the tool detects all peaks from 1px to 10 px.
However, Test 2 fails when testing to find peaks on the frame of an object plot i.e. an object (plot) on gray background.
The tool works well on white background.
Code
close all; clear all; clc;
f = figure;
hax = axes(f);
% Comment this out for Test 2
%zeroFigureDecorations(hax);
af = figure('Name', 'Do Not Touch');
x = rand(1,100);
y = rand(1,100);
linewidth=1;
plot(hax, x,y, 'LineWidth', linewidth);
I = getframe(hax);
I = I.cdata;
% https://se.mathworks.com/matlabcentral/fileexchange/37388-fast-2d-peak-finder
p=FastPeakFind(I);
% Input: 344x435x3 uint8
hold(hax, 'on');
plot(hax, p(1:2:end),p(2:2:end),'r+')
hold(hax, 'off');
function zeroFigureDecorations(ax)
axis(ax, 'tight');
set(ax, 'yTickLabel', []);
set(ax, 'xTickLabel', []);
set(ax, 'Ticklength', [0 0]); % http://stackoverflow.com/a/15529630/54964
colormap(ax, 1-gray(1024));
box(ax, 'off');
axis(ax, 'off');
end
Outputs in the following, and Fig. 1 shows that the function can detect something on lines when the background is white but not on correct locations.
Linewidth Output
10 166x1 double
1 844x1 double
Table: full axis decoration in Test 1
Linewidth Output
10 []
1 []
Table: no axis decorations, after zeroFigureDecorations(hax) in Test 2
Fig. 1 line as input (See Bla's answer) and its output,
Fig. 2 Output is wrong in Section 2,
Fig. 3 One more example that you cannot apply to function to simple curves,
Fig. 4 Section 3 Output is wrong, since not known how to apply the function on spectrograms
2 Test with bla's example data
f0 = figure;
hax0 = axes(f0);
d=uint16(conv2(reshape(single( 2^14*(rand(1,128*128)>0.9995) ),[128 128]) ,fspecial('gaussian', 10,2),'same')+2^4*rand(128));
imagesc(d, 'Parent', hax0);
I = getframe(hax0);
I = I.cdata;
p=FastPeakFind(I);
hold(hax0, 'on');
plot(hax0, p(1:2:end),p(2:2:end),'r+')
hold(hax0, 'off');
Output is wrong in Fig. 2
3 Testing with spectrograms
f3 = figure;
hax3 = axes(f3);
N = 1024*10;
n = 0:N-1;
w0 = 2*pi/5;
x = sin(w0*n)+10*sin(2*w0*n);
s = spectrogram(x);
spectrogram(x,'yaxis')
p=FastPeakFind(s);
hold on;
plot(p(1:2:end),p(2:2:end),'r+')
Matlab: 2016b
OS: Debian 8.5

You are not using the function correctly.
your code is this (verbatim):
f = figure;
hax = axes(f);
af = figure('Name', 'Do Not Touch');
x = rand(1,100);
y = rand(1,100);
linewidth=1;
plot(hax, x,y, 'LineWidth', linewidth);
I = getframe(hax);
I = I.cdata;
The matrix I is not a matrix that contain peaks like the function is intended to have. This is how it looks like:
imagesc(I);
Even if all you had were single pixels, that is not what the function is supposed to have, as it is said that the peaks point spread function needs to be larger than some # of pixels, and that they are assumed to be sparse. The function has a demonstration on a sample image that works fine.
Also , it's completely unclear what you even mean by peaks here.
EDIT:
Here's an example of how to use the function. First let's select random positions where we "create" the peaks:
I=rand(200)>0.9995;
This makes a binary matrix with only the points larger than 0.9995 selected (or having value 1). At each step you can imagesc(I) to see how I looks.
In real life, a camera will have some intensity in these points so we write:
I=I*100;
This is important as the peak by dentition needs to be a maximum value in its neighborhood. In real life, peaks are mostly not single pixels, they have some "width" or spread (this is also what the function says it deals with):
I=conv2(I,fspecial('gaussian',10,2),'same');
here, this spread is done by a "point-spread function" of a guassian of some width.
Lets add some 30% noise (note that after the last step the maximum value of the peaks is no longer 100, because it is spread to other pixels as well):
I=I+0.3*max(I(:))*rand(size(I));
Let's find peaks
p=FastPeakFind(I);
See how it did:
subplot(1,2,1);imagesc(I);
subplot(1,2,2);imagesc(I); hold on
plot(p(1:2:end),p(2:2:end),'r+')
In the function code, the example is doing what I wrote here in a single line. Note that there is an edg parameter, as this will not work on peaks on the edges of the image. This cab be solved by padding the image with zeros I think...

Related

Speeding up Octave / Matlab plot

Gnoivce and Hartmut helped a lot with this code but it takes a while to run.
The CData property in the bar command doesn't seem to be implemented in the Octave 4.0-4.2.1 version which I'm using. The work around for this was to plot all the single bars individually and set an individual color for each individual bar. People helped me out and got me this far but it takes 5 minutes for the plot to show does anyone know a way of speeding this up?
The following code runs:
marbles.jpg image file used below:
clear all,clf reset,tic,clc
rgbImage = imread('/tmp/marbles.jpg');
hsvImage = rgb2hsv(rgbImage); % Convert the image to HSV space
hPlane = 360.*hsvImage(:, :, 1); % Get the hue plane scaled from 0 to 360
binEdges = 0:360; %# Edges of histogram bins
N = histc(hPlane(:),binEdges); %# Bin the pixel hues from above
C = colormap(hsv(360)); %# create an HSV color map with 360 points
stepsize = 1; % stepsize 1 runs for a while...
for n=binEdges(2:stepsize:end) %# Plot the histogram, one bar each
if (n==1), hold on, end
h=bar(n,N(n));
set(h,'FaceColor',C(n,:)); %# set the bar color individually
end
axis([0 360 0 max(N)]); %# Change the axes limits
set(gca,'Color','k'); %# Change the axes background color
set(gcf,'Pos',[50 400 560 200]); %# Change the figure size
xlabel('HSV hue (in degrees)'); %# Add an x label
ylabel('Bin counts'); %# Add a y label
fprintf('\nfinally Done-elapsed time -%4.4fsec- or -%4.4fmins- or -%4.4fhours-\n',toc,toc/60,toc/3600);
Plot created after 5 mins:
To see original question original question
I'm guessing the loop is the bottleneck in your code that is taking so long? You could remove the loop and create the plot with one call to bar, then call set to modify the hggroup object and its child patch object:
h = bar(binEdges(1:end-1), N(1:end-1), 'histc'); % hggroup object
set(h, 'FaceColor', 'flat', 'EdgeColor', 'none');
hPatch = get(h, 'Children'); % patch object
set(hPatch, 'CData', 1:360, 'CDataMapping', 'direct');
Repeating your code with this fix renders right away for me in Octave 4.0.3:
As I suggested in a comment, I would use image (takes 0.12s on my system for your image).
EDIT: more comments, fix little bug, allow to create bins with stepsize > 1
img_fn = "17S9PUK.jpg";
if (! exist (img_fn, "file"))
disp ("downloading image from imgur.com...");
fflush (stdout);
urlwrite ("http://i.imgur.com/17S9PUK.jpg", "17S9PUK.jpg");
endif
rgbImage = imread (img_fn);
## for debugging so the matrixes fit on screen
if (0)
pkg load image
rgbImage = imresize (rgbImage, [6 8]);
endif
hsvImage = rgb2hsv(rgbImage);
hPlane = 360 .* hsvImage(:, :, 1);
## create bins, I've choosen 2 step to "smooth" the result
binEdges = 1:2:360;
N = histc (hPlane(:), binEdges)';
cm = permute (hsv (numel (binEdges)), [3 1 2]);
## Create an image with x = hue
img = repmat (cm, max(N), 1);
## Create binary mask which is used to black "img" dependent on N
sp = sparse (N(N > 0), (1:360)(N > 0), true, max(N), numel (binEdges));
mask = full (cumsum (flipud (sp)));
## extend mask in depth to suppress RGB
mask = repmat (mask, [1 1 3]);
## use inverted mask to "black out" pixels < N
img(logical (1 - flipud (mask))) = 0;
## show image
image (binEdges, 1:max(N), img)
set (gca, "ydir", "normal");
xlabel('HSV hue (in degrees)');
ylabel('Bin counts');
## print it for stackoverflow
print ("out.png")
Same as above but with bin width 1 (Elapsed time is 0.167423 seconds.)
binEdges = 1:360;

MATLAB/Octave plot markers above the line rather than on the line

I want to visualize the peaks of a function, and I want to have markers for it appear above the line they are associated with.
I fabricated a minimum example where I already have the peaks, the question is just how to visualize the markers correctly:
y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2]
x = (1:length(y))
plot(x,y);
hold on;
peaks = [3 7];
plot(x(peaks), y(peaks), 'v', 'MarkerSize', 24);
print('-dpng', 'example.png', '-S640,480');
So, as a result, the markers appear centered on the line like this:
The result that I want could be achieved by carefully tuning a parameter OFFSET like this:
plot(x(peaks), y(peaks)+OFFSET, 'v', 'MarkerSize', 24);
As shown in the following figure, for this exact example OFFSET=2.56 works for the exported png, but with the interactive plot and exporting vector graphics, it's wrong again.
Can anyone recommend a way to get this result without having to manually doing trial/error?
Currently I am using Octave with gnuplot to export to latex+tikz, and it would be good if the solution would work there.
In my actual (more complicated) use case I am plotting multiple lines after each other into the same figure, and the y limits change, so the offsets can not just be calculated easily, as the markersize doesn't change with the y limits.
Edit: Additionally I am using a semilogx plot, so drawing lines inside the diagram in the x/y-Axis scales would look distorted.
One way to do this is with annotations, but there are some drawbacks (see below).
Annotations enable you to place various graphic objects into your figure. One very annoying thing about them is that they work in so-called normalized coordinates,
which span the whole figure window (not just the plot area) and go from [0,0] to [1,1], forcing you to convert to these coordinates first. I wrote a simple function to do this, provided your plot scale is linear (if you want logarithmic, you will have to modify this function):
## Convert from data coordinates to normalized figure coordinates.
function [xf yf] = figcoords(xa, ya)
axp = get(gca, "position");
lf = axp(1);
bf = axp(2);
rf = lf + axp(3);
tf = bf + axp(4);
xl = xlim();
yl = ylim();
la = xl(1);
ra = xl(2);
ba = yl(1);
ta = yl(2);
xf = lf + (xa-la).*(rf-lf)./(ra-la);
yf = bf + (ya-ba).*(tf-bf)./(ta-ba);
endfunction
With this out of your way, you can proceed to annotating the plot using the annotation function:
y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2];
x = (1:length(y));
peaks = [3 7];
## Plot the data as you would normally
plot(x,y);
## Plot peak markers (no `hold on` needed)
[xp yp] = figcoords(peaks, y(peaks)); # Transform to figure coordinates
for coords = [xp; yp]
xpi = coords(1);
ypi = coords(2);
annotation("arrow", [xpi xpi], [ypi+eps ypi]);
endfor
Plot with annotated peaks
Here, we actually draw little arrows pointing from top onto the peaks.
As their height is very small, we only see the arrowheads.
The arguments to the annotation function are the x and y coordinates
of the endpoints of the arrow. Note that we added a small number (eps)
to the y-value of the starting point to make the arrow point downward.
If you want, you can tweak the appearance of the markers to make them more visually appealing:
y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2];
x = (1:length(y));
peaks = [3 7];
coloridx = get(gca, "ColorOrderIndex")
peakcolor = get(gca, "ColorOrder")(coloridx,:); # Save current plot colour
plot(x,y);
## Plot peak markers
[xp yp] = figcoords(peaks, y(peaks));
for coords = [xp; yp]
xpi = coords(1);
ypi = coords(2);
annotation("arrow", [xpi xpi], [ypi+eps ypi], "headstyle", "plain",...
"color", peakcolor);
endfor
Plot with annotated peaks in the same color
Drawbacks
While this approach works fine regardless of the size of the markers or your plot, there are some drawbacks:
First, the annotations are fixed relative to the figure window, not the plot.
This is fine when you display the plot for the first time, but once you zoom
or pan, the alignment is lost: The markes stay in place while the plot moves.
If you don't need an interactive plot (eg, you just want to export it to image),
just be sure to set the plot limits before adding the annotations and you should
be fine.
Second, this method is very slow compared to plotting the points using the
plot function. On my computer, for example, when drawing a simple example with
seven annotated peaks, it takes about a second before the markers appear.
Plotting a signal with thousands of peaks is near impossible.
Concerning the Matlab part, you could draw the peak markers yourself. Somewhere along these lines (extending your example):
y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2]
x = (1:length(y))
figure, plot(x,y);
leglengthx=0.2;
leglengthy=0.5;
hold on;
peaks = [3 7];
peaks_max=[10 24];
for ii=1:2
line([peaks(ii) peaks(ii)+leglengthx],[peaks_max(ii) peaks_max(ii)+leglengthy]);
line([peaks(ii) peaks(ii)-leglengthx],[peaks_max(ii) peaks_max(ii)+leglengthy]);
line([peaks(ii)-leglengthx peaks(ii)+leglengthx],[peaks_max(ii)+leglengthy peaks_max(ii)+leglengthy]);
end
plot(x(peaks), y(peaks), 'v', 'MarkerSize', 24);
I have added the maxima of the peaks, which should not be an issue to automatically extract and two variables that control the triangle size of the marker. And then its just drawing three lines for every peak.
I don't know how this will translate to Octave.
What about drawing the little triangles?
y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2];
x = (1:length(y));
peaks = [3 7];
plot(x,y);
hold on; line([peaks(1) peaks(1)+0.2], [y(x==peaks(1)) y(x==peaks(1))+1], 'color','b')
hold on; line([peaks(1) peaks(1)-0.2], [y(x==peaks(1)) y(x==peaks(1))+1], 'color','b')
hold on; line([peaks(1)+0.2 peaks(1)-0.2], [y(x==peaks(1))+1 y(x==peaks(1))+1], 'color','b')
hold on; line([peaks(2) peaks(2)+0.2], [y(x==peaks(2)) y(x==peaks(2))+1], 'color','b')
hold on; line([peaks(2) peaks(2)-0.2], [y(x==peaks(2)) y(x==peaks(2))+1], 'color','b')
hold on; line([peaks(2)+0.2 peaks(2)-0.2], [y(x==peaks(2))+1 y(x==peaks(2))+1], 'color','b')
There can be a problem if the y-values of the peaks exists in other locations on the vector. If so, you can specify first or other matching specs for the find function.

How do I create a polar plot with concentric colored rings corresponding to single values in Matlab?

I am trying to create a plot that looks like this with rings of constant values (colors) extending from 0 to 100 in 10 unit increments.
Rings of single values extending outward from center
However, my code is not producing this, and I do not know where it has gone wrong.
% values representing the colors that each ring should be
% starting from the center and moving outwards in 10 unit increments.
values = [364,358,354,348,339,335,330,325,320,310];
xCoord = linspace(0,2*pi,10);
yCoord = linspace(0,100,10);
[TH,R] = meshgrid(xCoord,yCoord);
[X,Y] = pol2cart(TH,R);
[Z] = meshgrid(values);
contour_ticks = 300:5:375;
figure
hold on
contourf(X,Y,Z,contour_ticks);
a=gca; cb=colorbar; colormap('jet'); caxis([300 375]);
This produces a plot resembling this:
Incorrect plot
Any ideas what I'm doing wrong? Any help is greatly appreciated. Thanks.
If you just want to plot circles, you can use the following approach:
radii = 100:-10:10; %// descending order, so that bigger circles don't cover small ones
colors = parula(numel(radii)); %// or use some other colormap
for n = 1:numel(radii)
r = radii(n);
rectangle('Position', [-r -r 2*r 2*r], 'Curvature', [1 1], 'FaceColor', colors(n,:),...
'EdgeColor', 'none') %// plot each circle using sequential colors, no edge
hold on
end
axis equal
axis([-1 1 -1 1]*max(radii))

Image Histogram Comparison

I was trying to do histogram image comparison between two RGB images which includes heads of the same persons and non-heads to see the correlation between them. The reason I am doing this is because after performing scanning using HOG to check whether the scanning window is a head or not, I am now trying to track the same head throughout consequent frames and also I want to remove some clear false positives.
I currently tried both RGB and HSV histogram comparison and used Euclidean Distance to check the difference between the histograms. The following is the code I wrote:
%RGB histogram comparison
%clear all;
img1 = imread('testImages/samehead_1.png');
img2 = imread('testImages/samehead_2.png');
img1 = rgb2hsv(img1);
img2 = rgb2hsv(img2);
%% calculate number of bins = root(pixels);
[rows, cols] = size(img1);
no_of_pixels = rows * cols;
%no_of_bins = floor(sqrt(no_of_pixels));
no_of_bins = 256;
%% obtain Histogram for each colour
% -----1st Image---------
rHist1 = imhist(img1(:,:,1), no_of_bins);
gHist1 = imhist(img1(:,:,2), no_of_bins);
bHist1 = imhist(img1(:,:,3), no_of_bins);
hFig = figure;
hold on;
h(1) = stem(1:256, rHist1);
h(2) = stem(1:256 + 1/3, gHist1);
h(3) = stem(1:256 + 2/3, bHist1);
set(h, 'marker', 'none')
set(h(1), 'color', [1 0 0])
set(h(2), 'color', [0 1 0])
set(h(3), 'color', [0 0 1])
hold off;
% -----2nd Image---------
rHist2 = imhist(img2(:,:,1), no_of_bins);
gHist2 = imhist(img2(:,:,2), no_of_bins);
bHist2 = imhist(img2(:,:,3), no_of_bins);
hFig = figure;
hold on;
h(1) = stem(1:256, rHist2);
h(2) = stem(1:256 + 1/3, gHist2);
h(3) = stem(1:256 + 2/3, bHist2);
set(h, 'marker', 'none')
set(h(1), 'color', [1 0 0])
set(h(2), 'color', [0 1 0])
set(h(3), 'color', [0 0 1])
%% concatenate values of a histogram in 3D matrix
% -----1st Image---------
M1(:,1) = rHist1;
M1(:,2) = gHist1;
M1(:,3) = bHist1;
% -----2nd Image---------
M2(:,1) = rHist2;
M2(:,2) = gHist2;
M2(:,3) = bHist2;
%% normalise Histogram
% -----1st Image---------
M1 = M1./no_of_pixels;
% -----2nd Image---------
M2 = M2./no_of_pixels;
%% Calculate Euclidean distance between the two histograms
E_distance = sqrt(sum((M2-M1).^2));
The E_distance consists of an array containing 3 distances which refer to the red histogram difference, green and blue.
The Problem is:
When I compare the histogram of a non-head(eg. a bag) with that of a head..there is a clear difference in the error. So this is acceptable and can help me to remove the false positive.
However! When I am trying to check whether the two images are heads of the same person, this technique did not help at all as the head of another person gave a less Euclidean distance than that of the same person.
Can someone explain to me if I am doing this correctly, or maybe any guidance of what I should do?
PS: I got the idea of the LAB histogram comparison from this paper (Affinity Measures section): People Looking at each other
Color histogram similarity may be used as a good clue for tracking by detection, but don't count on it to disambiguate all possible matches between people-people and people-non-people.
According to your code, there is one thing you can do to improve the comparison: currently, you are working with per-channel histograms. This is not discriminative enough because you do not know when R-G-B components co-occur (e.g. you know how many times the red channel is in range 64-96 and how many times the blue is in range 32-64, but not when these occur simultaneously). To rectify this, you must work with 3D histograms, counting the co-occurrence of colors). For a discretization of 8 bins per channel, your histograms will have 8^3=512 bins.
Other suggestions for improvement:
Weighted assignment to neighboring bins according to interpolation weights. This eliminates the discontinuities introduced by bin quantization
Hierarchical splitting of detection window into cells (1 cell, 4 cells, 16 cells, etc), each with its own histogram, where the histograms of different levels and cells are concatenated. This allows catching local color details, like the color of a shirt, or even more local, a shirt pocket/sleeve.
Working with the Earth Mover's Distance (EMD) instead of the Euclidean metric for comparing histograms. This reduces color quantization effects (differences in histograms are weighted by color-space distance instead of equal weights), and allows for some error in the localization of cells within the detection window.
Use other cues for tracking. You'll be surprised how much the similarity between the HoG descriptors of your detections helps disambiguate matches!

Xtick marks and Xtick labels on heatmap in Matlab

Environment: Windows 7 64 bit, Matlab 2014a
Objective:
To draw a heatmap of errors for two parameters to be optimized.
To put proper tick marks and tick values
Draw the grid lines in the correct place
Problem: Arranging the X and Y tick positions and values. When the last ("end") value of the vectors in x and y axes are the same, the code I use puts the ticks and values properly. However, when the end values are different it does not, and produces something really weird.
Below I have included the code which I have modified so that you can run it without the need of adding anything. Of course in my case the error vector are the error values, not random numbers. To see the problem of "end value" use the second b vector.
fontsize = 20
k = [2^-5, 2^-3, 2^-1, 2^0, 2^1, 2^3, 2^5, 2^7, 2^9, 2^11, 2^13, 2^15]
b = [2^-5, 2^-3, 2^-1, 2^0, 2^1, 2^3, 2^5, 2^7, 2^8, 2^9, 2^10, 2^11, 2^13, 2^15]
% b = [2^-5, 2^-3, 2^-1, 2^0, 2^1, 2^3, 2^5, 2^7, 2^8, 2^9, 2^10, 2^11, 2^13, 2^19]
errorVector = randi(20, 1, length(b)*length(k))'
figure
% Create a matrix from error vector (size of error vector is [length(k)*length(b),1])
B = reshape(errorVector, [length(b), length(k)])
B = flipud(B)
% imagesc(x,y,C)
imagesc(b, k, B)
title('Heatmap Parameters Percent Error', 'FontSize', fontsize);
% Set colorbar limits
caxis([0 15])
colorbar;
ax1 = gca;
xTickLabel = (k)'
xTick = linspace(k(1), k(end), numel(xTickLabel))';
set(ax1, 'XTick', xTick, 'XTickLabel', xTickLabel)
xlabel('k parameter', 'FontSize', fontsize)
yTickLabel = (b)'
yTick = linspace(b(1), b(end), numel(yTickLabel))';
set(ax1, 'YTick', yTick, 'YTickLabel', flipud(yTickLabel(:)))
ylabel('b parameter', 'FontSize', fontsize)
set(ax1,'FontSize', fontsize)
Here, change any of the end values of b or k vectors, and the program will output a graph where the X and Y ticks are totally wrong.
Also, I would like to draw grid lines. When I use "grid on" it draws grid lines right on the tick marks which is not correct in the case of heatmap. Because tick marks will be in the center of the columns -or rows- but the grid lines should be at the boundaries between the columns -or rows.
By the way if you know a better way to plot a heatmap in Matlab, please do tell.
Please help me solve this problem. Any help is appreciated,
Ilyas
First of all - you don't need to flipud the B matrix - by default imagesc plots the y-data inversed, but you can fix this with the following statement:
set(gca,'ydir','normal');
This also means you don't have to mess around with flipping the tick-labels. You can get the labels right by doing the following:
% replace the imagesc call with:
imagesc(B);
set(gca,'ydir','normal');
% formatting stuff
...
% replace the set commands with:
set(ax1, 'XTick', 1:length(k), 'XTickLabel', k)
set(ax1, 'YTick', 1:length(b), 'YTickLabel', b)
By default, if you don't provide x and y data to the imagesc command, it will number them linearly (1,2,3...). Basically what we've done here is make sure that it has ticks for each of the elements of b and k, and then set the labels to the values of the respective vectors.
Unfortunately, I'm not sure if there is a way to get the grid spacing right with imagesc or not. You could try using pcolor instead, which has it's own set of issues, but allows you to get grid lines (of sorts) between the elements. It also allows you to use an interpolated shading mode, which will make your plot look more like a typical heat map.
To use pcolor instead, you just have to replace imagesc:
% imagesc(B);
% set(gca,'ydir','normal');
pcolor(B);
% this uses a smoother shading method, for a more 'heatmap' like output
% shading interp
Everything else should work as intended, I believe.