I have a sequence of nFrames images from a video, and I have computed the SIFT keypoints and descriptors from all of them and stored them in two cell arrays. What I need to do now is to compute the vector between the 1st keypoint on image 1 and the corresponding keypoint on image 2 (the corresponding keypoint will be the one with the same -or most similar- descriptors), and do so for all other keypoints in order to plot a graph showing how the keypoints move on the scene, which reflects the movement of the objects on the scene.
The keypoints are stored as a 4-by-n array (the first row are the x components, the second row are the y components -the other 2 rows are the scale and angle, which I don't need-, and each column corresponds to a different keypoint), so I thought of subtracting the corresponding points from the first image to the second image, but wouldn't I end up having just another point in the middle of those two? How can I store the difference as a vector in order to plot all of them later on on the same graph?
At the moment all I have is this:
% Clear all and add all folders to the current path
clear; close all; clc;
addpath(genpath('.'));
% Set path, nFrames and threshold values
path = 'img/record_tennis';
d = dir([ path,'\*.png' ]);
nFrames = length( d( not([ d.isdir ]) ) );
th = 0.01;
step = 20;
keypts = cell(1,nFrames);
desc = cell(1,nFrames);
% Main loop
for i = 1:step:nFrames
disp([ 'Processing frame number ',num2str(i),' of ',num2str(nFrames),'...' ]);
% Read the current image
imgRGB = imread([ path,'/',d(i).name ]);
img = sum( double(imgRGB),3 ) / 3 / 255;
% Perform SIFT on the current image and plot the keypoints
[ keypts{1,i},desc{1,i} ] = sift( img,'Threshold',th );
imshow( img ), hold on
plot( keypts{1,i}(1,:),keypts{1,i}(2,:),'.' );
pause(1); clc;
end
% Remove empty cells
keypts = keypts( ~cellfun( #isempty,keypts ) );
desc = desc( ~cellfun( #isempty,desc ) );
With this I compute the SIFT keypoints and descriptors from each frame (I have added a step value because the movement is very small and this way it is more noticeable between consecutive images). Then I remove the empty cells due to the step factor, and now I have to compute the vector between corresponding keypoints, but I am stuck with that.
In general, if you want the vector that goes from one set of points to another, you can simply subtract the two (as you mentioned). This will provide you a vector that is [dx, dy]. In order to display that vector field, you could then use quiver which will draw each of these vectors starting from the initial point. The inputs to quiver are:
quiver(initial_x, initial_y, dx, dy, scale)
So if we look at your variables, we can construct each of these variables from your keypts variable.
initial_x = keypts{1}(1,:);
initial_y = keypts{1}(2,:);
dx = keypts{current_frame}(1,:) - initial_x;
dy = keypts{current_frame}(2,:) - initial_y;
Now you should be able to plot this on top of your image to visualize the motion field.
hax = axes();
imshow(img, 'Parent', hax);
hold(hax, 'on')
q = quiver(initial_x, initial_y, dx, dy, 1);
Now, this requires that the points within keypts actually have correspondence between different frames (i.e. keypts{1}(:,1) is the same physical point as keypts{2}(:,1)). This may or may not be true depending on the function that you are calling.
Related
Im trying to create a project in MATLAB where i project a heat map matrix on to a cylinder in MATLAB but i'm bumping in to all kinds of issues.
Im using MATLAB version R2019b(Latest version).
Now my first issue id like to adress is is the modells being absolutely tiny even when i zoom in to the max.
Is there any way to make these models larger or to display them in a separate window ??
Also my second question is the regarding the scaling. As you can see the scale on the Z-axis of the cylinder goes to 512. any way to get a larger more detailed image where i can see each point on that scale?
clear vars
filename =
['/Users/gomer/Documents/Datavisualisering/Projekt/data/day/filenames.txt'];
%This line simply gives us a table of all filenames in this file.
T = readtable(filename);
tsize = size(T);
%extracts the content of the table as a categorical array.
%%
%
% {rownumber,variablenumber}. {100,1} = row 100, variable 1.
%converts the categorical array into a string array.
%joins the string array across the column.
%string(T{100:105,1}); implies from row 100 to row 105 and use variable 1.
%This line simply adds the name of the file at row 100 to the path of the
%file. Hnece we get a full filepath.
filename = strcat('/Users/gomer/Documents/Datavisualisering/Projekt/data/day/', string(T{100,1}));
map100 = getHeatMap(filename);
%%
%
filename = strcat('/Users/gomer/Documents/Datavisualisering/Projekt/data/day/', string(T{1000,1}));
map1000 = getHeatMap(filename);
%creates a image
k=imshow(map100);
%creates a colormap.
%gca returns the current axes (or standalone visualization) in the current figure.
%hence the command just works top down and affects last image displayed.
colormap(gca, 'jet');
k=imshow(map1000);
colormap(gca, "jet");
function heat = getHeatMap(filename)
%Returns a struct with info about the file.
s = dir(filename);
%Opens a file for reading, hence the 'r'.
fin=fopen(filename,'r');
%A = fread(fileID,sizeA,precision)
%reads file data into an array, A, with dimensions, sizeA, and positions the file pointer after the last value read.
%fread populates A in column order.
I=fread(fin,s.bytes,'uint8=>uint8');
%The uint16 function converts a Input array, specified as a scalar, vector, matrix, or multidimensional array.
%Converts the values in to type uint16 and creates a Matrix with the values.
%Looks more like we are just calculating the size of values to be
%deducted from matrix size (no idea why).
w = uint16(I(1))+256*uint16(I(2));
h = uint16(I(3))+256*uint16(I(4));
skip = s.bytes - w*h + 1;
IN = I(skip:1:s.bytes);
%reshape(A,sz) reshapes A using the size vector, sz, to define size(B). For example, reshape(A,[2,3]) reshapes A into a 2-by-3 matrix.
%sz must contain at least 2 elements, and prod(sz) must be the same as numel(A).
%single(X) converts the values in X to single precision.
Z=single(reshape(IN,w,h));
%Interpolate the values.
%telling the system which points to interpolate in between.
Z=griddedInterpolant(Z');
y_range = linspace(1.0,single(h),360);
x_range = linspace(1.0,single(w),512);
%Used to apecify the query points (points in between which we want
%interpolation) as vectors (this is to get a more continues image).
%specifies the query points as grid vectors.
%Use this syntax to conserve memory when you want to query a large grid of points.
heat = uint8(Z({y_range, x_range}));
end
Thank you
Resizing Figure Window
Copying your script to a .m and running it instead of a live .mlx file will automatically create all the figures in a separate window by default. To adjust the size of the figures the following the position property can be modified. To adjust the scale functions xticks(), yticks() and zticks() can be used. These three scale functions take in an array representing all the line markers along the respective axis/scale.
Test Plot Script:
X_Position = 10;
Y_Position = 10;
Width = 1000;
Height = 500;
%Configuring the figure settings%
figure('Position', [X_Position Y_Position Width Height])
%Test plot (replace with your plot)%
Data = [1 2 3 4 5 6];
plot(Data);
Plotting Heatmaps on a Cylindrical Surfaces
Method 1: Using warp() Function
Haven't delved into which orientation the data should be set as but this is an option to use the warp() function to wrap the heatmap around a cylinder. They're most likely other 3D plotting options if specific points are of interest. The points of the cylinder to be plotted are generated using the cylinder() function which returns the xyz-coordinates of the cylinder's wireframe. The fourth input argument into the warp() function serves and a colormap in this case it is proportional to the heatmap values.
load('HeatMapMatrix.mat');
%Setting up the figure%
clf;
close all;
figure('Position', [10 10 1000 500])
%Creating the cylinder%
Cylinder_Radius = 360;
Cylinder_Height = 512;
[X,Y,Z] = cylinder(Cylinder_Radius,Cylinder_Height-1);
%Warping the heatmap along the cylinder%
subplot(1,2,1); warp(X,Y,Cylinder_Height.*Z,map100');
colormap(gca,'jet');
subplot(1,2,2); warp(X,Y,Cylinder_Height.*Z,map100);
colormap(gca,'jet');
Method 2: Plotting All Points and Using surf() Function:
In this case, the coordinates for a cylinder are generated by first finding the coordinates of the circumference of the cylinder. This is done by using the sin() and cos() relationships:
X-Points = Radius × cos(𝜃)
Y-Points = Radius × sin(𝜃)
This results in the xy-coordinates of the cylinder's circle. These xy-coordinates need to be duplicated using repmat() to be later used for the varying heights. The process can be described best with a diagram as follows:
Four matrices above are created to plot each Heat Data point corresponding to an xyz-coordinate. The x and y coordinates are repeated in every row of matrices X-Points and Y_Points since those are constant for the repeating circles. The columns of the matrix Z-Points are also duplicates of each other since the heights should be constant for each row corresponding to each circle.
load('HeatMapMatrix.mat');
Radius = 20;
Number_Of_Data_Points = 360;
Theta = linspace(0,2*pi,Number_Of_Data_Points);
%The xy values according to radius and number of points%
X_Points = Radius*cos(Theta);
Y_Points = Radius*sin(Theta);
map100 = rot90(map100);
Sample_Range = 255 - 0;
Temperature_Range = 450 - 50;
Multiplier = Temperature_Range/Sample_Range;
map100 = map100.*Multiplier + 50;
Height = 512;
X_Points = repmat(X_Points,Height,1);
Y_Points = repmat(Y_Points,Height,1);
Z_Points = (1:512)';
Z_Points = repmat(Z_Points,1,Number_Of_Data_Points);
clf;
close;
figure('Position', [10 10 800 500])
Offset = 200;
subplot(1,3,1:2); Surface = surf(Y_Points,X_Points,Z_Points,'Cdata',map100);
title("3D Heatmap Plot");
zlabel("Height");
shading interp
colorbar
% direction = [0 1 0];
% rotate(Surface,direction,90)
Maximum_Value = 450;
Minimum_Value = 50;
caxis([Minimum_Value Maximum_Value]);
subplot(1,3,3); imshow(rot90(rot90(map100)));
colormap(gca, 'default');
title("Flat Heatmap Plot");
caxis([Minimum_Value Maximum_Value]);
colorbar;
Ran using MATLAB R2019b
I am attempting to extract the Radon Signature in order to recognize patterns of clothing (striped,plaid, irregular and patternless) as done in 1.
Algorithm to be implemented :
1. Use sobel operator to compute the gradient map as f(x,y).
2. Perform Radon transform based on maximum disk area.
3. Compute the variance of r under all theta directions.
4. Employ L2-norm to normalize the feature vector.
5. Plot Radon Signature as a bar chart of var(r) for all theta values.
I have done the following :
img = imread('plaid.jpg');
grey = rgb2gray(img);
img2 = edge(grey, 'sobel');
vararray=zeros(1,size(theta,2));
theta = -89:90;
for j = 1: size(theta,2)
[R3,xp3] = radon (img2,theta(j));
vararray(j) = var(R3);
end
vararray = vararray/norm(vararray);
figure(1), bar(theta,vararray),title('Radon Signature');
I believe that my error lies in the first 2 steps. I am unsure how to perform Radon only on the maximum disk area.
My results are shown on the right, while from the article (referenced below) is shown on the left.
However, my results should at least show 2 distinct peaks as shown in the acticle's results, but they do not.
Any assistance is appreciated.
Source of Algorithm : "Assistive Clothing Pattern Recognition for Visually Impaired People" by Xiaodong Yang, Student Member, IEEE, Shuai Yuan, and YingLi Tian, Senior Member, IEEE
Maximum disk area is, as #beaker thought, defined by the maximum filled circle that fits inside the bounding box of the image. That you can observe from the Fig.3 b) of the article.
Another thing you did wrong, is using edge detector edge(grey, 'sobel') while you should use gradient map or more formally gradient magnitude. Here's a code which produces a curve close to what is shown in Fig 3d. How to quantify it to six peaks, remains a question.
A = imread( 'Layer-5.png' ); % image from the article
A = double(rgb2gray( A ));
% gradient magnitude
dx = imfilter(A,fspecial('sobel') ); % x, 3x3 kernel
dy = imfilter(A,fspecial('sobel')'); % y
gradmag = sqrt( dx.^2 + dy.^2 );
% mask by disk
R = min( size(A)/2 ); % radius
disk = insertShape(zeros(size(A)),'FilledCircle', [size(A)/2,R] );
mask = double(rgb2gray(disk)~=0);
gradmag = mask.*gradmag;
% radon transform
theta = linspace(0,180,180);
vars = zeros(size(theta));
for u = 1:length(theta)
[rad,xp] =radon( gradmag, theta(u) );
indices = find( abs(xp)<R );
% ignore radii outside the maximum disk area
% so you don't sum up zeroes into variance
vars(u) = var( rad( indices ) );
end
vars = vars/norm(vars);
figure; plot( vars );
Bear in mind, images copied from the article appear with jpg artefacts. After good denoising (a tad too much here), e.g.,
you get much more prominent results.
I have implemented in Matlab a bandpass filter for a 4D image (4D matrix). The first three dimensions are spatial dimensions, the last dimension is a temporal one. Here is the code:
function bandpass_img = bandpass_filter(img)
% Does bandpass filtering on input image
%
% Input:
% img: 4D image
%
% Output:
% bandpass_img: Bandpass filtered image
TR = 1; % Repetition time
n_vols = size(img,3);
X = [];
% Create matrix (voxels x time points)
for z = 1:size(img,3)
for y = 1:size(img,2)
for x = 1:size(img,1)
X = [X; squeeze(img(x,y,z,:))']; %#ok<AGROW>
end
end
end
Fs = 1/TR;
nyquist = 0.5*Fs;
% Pass bands
F = [0.01/nyquist, 0.1/nyquist];
type = 'bandpass';
% Filter order
n = floor(n_vols/3.5);
% Ensure filter order is odd for bandpass
if (mod(n,2) ~= 0), n=n+1; end
fltr = fir1(n, F, type);
% Looking at frequency response
% freqz(fltr)
% Store plot to file
% set(gcf, 'Color', 'w');
% export_fig('freq_response', '-png', '-r100');
% Apply to image
X = filter(fltr, 1, X);
% Reconstructing image
i = 1;
bandpass_img = zeros(size(img));
for z = 1:size(img,3)
for y = 1:size(img,2)
for x = 1:size(img,1)
bandpass_img(x,y,z,:) = X(i,:)';
i = i + 1;
end
end
end
end
I'm not sure if the implementation is correct. Could somebody verify it or does somebody find a failure?
Edit: Thanks to SleuthEye it now kind of works when I'm using bandpass_img = filter(fltr, 1, img, [], 4);. But there is still a minor problem. My images are of size 80x35x12x350, i.e. there are 350 time points. I have plotted the average time series before and after applying the bandpass filter.
Before bandpass filtering:
After bandpass filtering:
Why is this peak at the very beginning of the filtered image?
Edit 2: There is now a peak at the beginning and at the end. See:
I have made a second plot where I marked each point with a *. See:
So the first and last two time points seem to be lower.
It seems that I have to remove 2 time points at the beginning and also 2 time points at the end, so in total I will loose 4 time points.
What do you think?
Filtering a concatenation of all the elements using the 1-D filter function as you are doing is likely going to result in a distorted image as the smoothing makes the end of each row blend into the beginning of the next one. Unless you are trying to obtain a psychedelic rendition of your 4D data, this is unlikely to do what you are expecting it to.
Now, according to Matlab's filter documentation:
y = filter(b,a,x,zi,dim) acts along dimension dim. For example, if x is a matrix, then filter(b,a,x,zi,2) returns the filtered data for each row.
So, to smooth out the 3D images over time (which you indicated is the fourth dimension of you matrix img), you should use
bandpass_img = filter(fltr, 1, img, [], 4);
Used as such, the signal would starts a 0 because that's the default initial state of the filter and the filter takes a few samples to ramp up. If you know the value of the initial state you can specify that with the zi argument (the 4th argument):
bandpass_img = filter(fltr, 1, img, zi, 4);
Otherwise, a typical order N linear FIR filter has a delay of N/2 samples. To get rid of the initial ramp up you can thus just discard those N/2 initial samples:
bandpass_img = bandpass_img(:,:,:,(N/2+1):end);
Similarly, if you want to see the output corresponding to the last N/2 input values, you'd have to pad your input with N/2 extra samples (zeros will do):
img = padarray(img, [0 0 0 N/2], 0, 'post');
I am working on code that select set of pixels randomly from gray images, then comparing the intensity of each 2 pixels by subtracting the intensity of pixel in one location from another one in different location.
I have code do random selection, but I am not sure of this code and I do not know how to do pixels subtraction?
thank you in advance..
{
N = 100; % number of random pixels
im = imread('image.bmp');
[nRow,nCol,c] = size(im);
randRow = randi(nRow,[N,1]);
randCol = randi(nCol,[N,1]);
subplot(2,1,1)
imagesc(im(randRow,randCol,:))
subplot(2,1,2)
imagesc(im)
}
Parag basically gave you the answer. In order to achieve this vectorized, you need to use sub2ind. However, what I would do is generate two sets of rows and columns. The reason why is because you need one set for the first set of pixels and another set for the next set of pixels so you can subtract the two sets of intensities. Therefore, do something like this:
N = 100; % number of random pixels
im = imread('image.bmp');
[nRow,nCol,c] = size(im);
%// Generate two sets of locations
randRow1 = randi(nRow,[N,1]);
randCol1 = randi(nCol,[N,1]);
randRow2 = randi(nRow,[N,1]);
randCol2 = randi(nCol,[N,1]);
%// Convert each 2D location into a single linear index
%// for vectorization, then subtract
locs1 = sub2ind([nRow, nCol], randRow1, randCol1);
locs2 = sub2ind([nRow, nCol], randRow2, randCol2);
im_subtract = im(locs1) - im(locs2);
subplot(2,1,1)
imagesc(im_subtract);
subplot(2,1,2)
imagesc(im);
However, the above code only assumes that your image is grayscale. If you want to do this for colour, you'll have to do a bit more work. You need to access each channel and subtract on a channel basis. The linear indices that were defined above are just for a single channel. As such, you'll need to offset by nRow*nCol for each channel if you want to access the same corresponding locations in the next channels. As such, I would use sub2ind in combination with bsxfun to properly generate the right values for vectorizing the subtraction. This requires just a slight modification to the above code. Therefore:
N = 100; % number of random pixels
im = imread('image.bmp');
[nRow,nCol,c] = size(im);
%// Generate two sets of locations
randRow1 = randi(nRow,[N,1]);
randCol1 = randi(nCol,[N,1]);
randRow2 = randi(nRow,[N,1]);
randCol2 = randi(nCol,[N,1]);
%// Convert each 2D location into a single linear index
%// for vectorization
locs1 = sub2ind([nRow, nCol], randRow1, randCol1);
locs2 = sub2ind([nRow, nCol], randRow2, randCol2);
%// Extend to as many channels as we have
skip_ind = permute(0:nRow*nCol:(c-1)*nRow*nCol, [1 3 2]);
%// Create 3D linear indices
locs1 = bsxfun(#plus, locs1, skip_ind);
locs2 = bsxfun(#plus, locs2, skip_ind);
%// Now subtract the locations
im_subtract = im(locs1) - im(locs2);
subplot(2,1,1)
imagesc(im_subtract);
subplot(2,1,2)
imagesc(im);
Background:
My question relates to extracting feature from an electrophoresis gel (see below). In this gel, DNA is loaded from the top and allowed to migrate under a voltage gradient. The gel has sieves so smaller molecules migrate further than longer molecules resulting in the separation of DNA by length. So higher up the molecule, the longer it is.
Question:
In this image there are 9 lanes each with separate source of DNA. I am interested in measuring the mean location (value on the y axis) of each lane.
I am really new to image processing, but I do know MATLAB and I can get by with R with some difficulty. I would really appreciate it if someone can show me how to go about finding the mean of each lane.
Here's my try. It requires that the gels are nice (i.e. straight lanes and the gel should not be rotated), but should otherwise work fairly generically. Note that there are two image-size-dependent parameters that will need to be adjusted to make this work on images of different size.
%# first size-dependent parameter: should be about 1/4th-1/5th
%# of the lane width in pixels.
minFilterWidth = 10;
%# second size-dependent parameter for filtering the
%# lane profiles
gaussWidth = 5;
%# read the image, normalize to 0...1
img = imread('http://img823.imageshack.us/img823/588/gele.png');
img = rgb2gray(img);
img = double(img)/255;
%# Otsu thresholding to (roughly) find lanes
thMsk = img < graythresh(img);
%# count the mask-pixels in each columns. Due to
%# lane separation, there will be fewer pixels
%# between lanes
cts = sum(thMsk,1);
%# widen the local minima, so that we get a nice
%# separation between lanes
ctsEroded = imerode(cts,ones(1,minFilterWidth));
%# use imregionalmin to identify the separation
%# between lanes. Invert to get a positive mask
laneMsk = ~repmat(imregionalmin(ctsEroded),size(img,1),1);
Image with lanes that will be used for analysis
%# for each lane, create an averaged profile
lblMsk = bwlabel(laneMsk);
nLanes = max(lblMsk(:));
profiles = zeros(size(img,1),nLanes);
midLane = zeros(1,nLanes);
for i = 1:nLanes
profiles(:,i) = mean(img.*(lblMsk==i),2);
midLane(:,i) = mean(find(lblMsk(1,:)==i));
end
%# Gauss-filter the profiles (each column is an
%# averaged intensity profile
G = exp(-(-gaussWidth*5:gaussWidth*5).^2/(2*gaussWidth^2));
G=G./sum(G);
profiles = imfilter(profiles,G','replicate'); %'
%# find the minima
[~,idx] = min(profiles,[],1);
%# plot
figure,imshow(img,[])
hold on, plot(midLane,idx,'.r')
Here's my stab at a simple template for an interactive way to do this:
% Load image
img = imread('gel.png');
img = rgb2gray(img);
% Identify lanes
imshow(img)
[x,y] = ginput;
% Invert image
img = max(img(:)) - img;
% Subtract background
[xn,yn] = ginput(1);
noise = img((yn-2):(yn+2), (xn-2):(xn+2));
noise = mean(noise(:));
img = img - noise;
% Calculate means
means = (1:size(img,1)) * double(img(:,round(x))) ./ sum(double(img(:,round(x))), 1);
% Plot
hold on
plot(x, means, 'r.')
The first thing to do to is convert your RGB image to grayscale:
gr = rgb2gray(imread('gelk.png'));
Then, take a look at the image intensity histogram using imhist. Notice anything funny about it? Use imcontrast(imshow(gr)) to pull up the contrast adjustment tool. I found that eliminating the weird stuff after the major intensity peak was beneficial.
The image processing task itself can be divided into several steps.
Separate each lane
Identify ('segment') the band in each lane
Calculate the location of the bands
Step 1 can be done "by hand," if the lane widths are guaranteed. If not, the line detection offered by the Hough transform is probably the way to go. The documentation on the Image Processing Toolbox has a really nice tutorial on this topic. My code recapitulates that tutorial with better parameters for your image. I only spent a few minutes with them, I'm sure you can improve the results by tuning the parameters further.
Step 2 can be done in a few ways. The easiest technique to use is Otsu's method for thresholding grayscale images. This method works by determining a threshold that minimizes the intra-class variance, or, equivalently, maximizes the inter-class variance. Otsu's method is present in MATLAB as the graythresh function. If Otsu's method isn't working well you can try multi-level Otsu or a number of other histogram based threshold determination methods.
Step 3 can be done as you suggest, by calculating the mean y value of the segmented band pixels. This is what my code does, though I've restricted the check to just the center column of each lane, in case the separation was off. I'm worried that the result may not be as good as calculating the band centroid and using its location.
Here is my solution:
function [locations, lanesBW, lanes, cols] = segmentGel(gr)
%%# Detect lane boundaries
unsharp = fspecial('unsharp'); %# Sharpening filter
I = imfilter(gr,unsharp); %# Apply filter
bw = edge(I,'canny',[0.01 0.3],0.5); %# Canny edges with parameters
[H,T,R] = hough(bw); %# Hough transform of edges
P = houghpeaks(H,20,'threshold',ceil(0.5*max(H(:)))); %# Find peaks of Hough transform
lines = houghlines(bw,T,R,P,'FillGap',30,'MinLength',20); %# Use peaks to identify lines
%%# Plot detected lines above image, for quality control
max_len = 0;
imshow(I);
hold on;
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','green');
%# Plot beginnings and ends of lines
plot(xy(1,1),xy(1,2),'x','LineWidth',2,'Color','yellow');
plot(xy(2,1),xy(2,2),'x','LineWidth',2,'Color','red');
%# Determine the endpoints of the longest line segment
len = norm(lines(k).point1 - lines(k).point2);
if ( len > max_len)
max_len = len;
end
end
hold off;
%%# Use first endpoint of each line to separate lanes
cols = zeros(length(lines),1);
for k = 1:length(lines)
cols(k) = lines(k).point1(1);
end
cols = sort(cols); %# The lines are in no particular order
lanes = cell(length(cols)-1,1);
for k = 2:length(cols)
lanes{k-1} = im2double( gr(:,cols(k-1):cols(k)) ); %# im2double for compatibility with greythresh
end
otsu = cellfun(#graythresh,lanes); %# Calculate threshold for each lane
lanesBW = cell(size(lanes));
for k = 1:length(lanes)
lanesBW{k} = lanes{k} < otsu(k); %# Apply thresholds
end
%%# Use segmented bands to determine migration distance
locations = zeros(size(lanesBW));
for k = 1:length(lanesBW)
width = size(lanesBW{k},2);
[y,~] = find(lanesBW{k}(:,round(width/2))); %# Only use center of lane
locations(k) = mean(y);
end
I suggest you carefully examine not only each output value, but the results from each step of the function, before using it for actual research purposes. In order to get really good results, you will have to read a bit about Hough transforms, Canny edge detection and Otsu's method, and then tune the parameters. You may also have to alter how the lanes are split; this code assumes that there will be lines detected on either side of the image.
Let me add another implementation similar in concept to that of #JohnColby's, only without the manual user-interaction:
%# read image
I = rgb2gray(imread('gele.png'));
%# middle position of each lane
%# (assuming lanes are somewhat evenly spread and of similar width)
x = linspace(1,size(I,2),10);
x = round( (x(1:end-1)+x(2:end))./2 );
%# compute the mean value across those columns
m = mean(I(:,x));
%# find the y-indices of the mean values
[~,idx] = min( bsxfun(#minus, double(I(:,x)), m) );
%# show the result
figure(1)
imshow(I, 'InitialMagnification',100, 'Border','tight')
hold on, plot(x, idx, ...
'Color','r', 'LineStyle','none', 'Marker','.', 'MarkerSize',10)
and applied on the smaller image: