5

Currently I have been working on obtaining the length of a curve, with the following code I have managed to get the length of a curve present in an image.

test image one curve

Then I paste the code that I used to get the length of the curve of a simple image. What I did is the following:

  1. I got the columns and rows of the image
  2. I got the columns in x and the rows in y
  3. I obtained the coefficients of the curve, based on the formula of the parable
  4. Build the equation
  5. Implement the arc length formula to obtain the length of the curve

    grayImage = imread(fullFileName);
    [rows, columns, numberOfColorBands] = size(grayImage);
    if numberOfColorBands > 1
      grayImage = grayImage(:, :, 2); % Take green channel.
    end
    subplot(2, 2, 1);
    imshow(grayImage, []);
    
    % Get the rows (y) and columns (x).
    [rows, columns] = find(binaryImage);
    
    coefficients = polyfit(columns, rows, 2); % Gets coefficients of the    formula.
    
    % Fit a curve to 500 points in the range that x has.
    fittedX = linspace(min(columns), max(columns), 500);
    
    % Now get the y values.
    fittedY = polyval(coefficients, fittedX);
    
    % Plot the fitting:
    
    subplot(2,2,3:4);
    plot(fittedX, fittedY, 'b-', 'linewidth', 4);
    grid on;
    xlabel('X', 'FontSize', fontSize);
    ylabel('Y', 'FontSize', fontSize);
    % Overlay the original points in red.
    hold on;
    plot(columns, rows, 'r+', 'LineWidth', 2, 'MarkerSize', 10)
    
    formula = poly2sym([coefficients(1),coefficients(2),coefficients(3)]);
    % formulaD = vpa(formula)
    df=diff(formula);
    df = df^2;
    
    f= (sqrt(1+df));
    
    i = int(f,min(columns),max(columns));
    j = double(i);
    disp(j);
    

Now I have the image 2 which has n curves, I do not know how I can do to get the length of each curve

test image n curves

AlexZ
  • 229
  • 1
  • 8

4 Answers4

2

I suggest you to look at Hough Transformation: https://uk.mathworks.com/help/images/hough-transform.html

You will need Image Processing Toolbox. Otherwise, you have to develop your own logic.

https://en.wikipedia.org/wiki/Hough_transform

Update 1

I had a two-hour thinking about your problem and I'm only able to extract the first curve. The problem is to locate the starting points of the curves. Anyway, here is the code I come up with and hopefully will give you some ideas for further development.

clc;clear;close all;

grayImage = imread('2.png');
[rows, columns, numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
    grayImage = grayImage(:, :, 2); % Take green channel.
end

% find edge.
bw = edge(grayImage,'canny');
imshow(bw);

[x, y] = find(bw == 1);
P = [x,y];

% For each point, find a point that is of distance 1 or sqrt(2) to it, i.e.
% find its connectivity.
cP = cell(1,length(x));
for i = 1:length(x)
    px = x(i);
    py = y(i);
    dx = x - px*ones(size(x));
    dy = y - py*ones(size(y));
    distances = (dx.^2 + dy.^2).^0.5;
    cP{i} = [x(distances == 1), y(distances == 1);
        x(distances == sqrt(2)), y(distances == sqrt(2))];
end

% pick the first point and a second point that is connected to it.
fP = P(1,:);
Q(1,:) = fP;
Q(2,:) = cP{1}(1,:);
m = 2;
while true
    % take the previous point from point set Q, when current point is
    % Q(m,1)
    pP = Q(m-1,:);
    % find the index of the current point in point set P.
    i = find(P(:,1) == Q(m,1) & P(:,2) == Q(m,2));
    % Find the distances from the previous points to all points connected
    % to the current point.
    dx = cP{i}(:,1) - pP(1)*ones(length(cP{i}),1);
    dy = cP{i}(:,2) - pP(2)*ones(length(cP{i}),1);
    distances = (dx.^2 + dy.^2).^0.5;
    % Take the farthest point as the next point.
    m = m+1;
    p_cache = cP{i}(find(distances==max(distances),1),:);
    % Calculate the distance of this point to the first point.
    distance = ((p_cache(1) - fP(1))^2 + (p_cache(2) - fP(2))^2).^0.5;
    if distance == 0 || distance == 1
        break;
    else
        Q(m,:) = p_cache;
    end
end
% By now we should have built the ordered point set Q for the first curve.
% However, there is a significant weakness and this weakness prevents us to
% build the second curve.

Update 2

Some more work since the last update. I'm able to separate each curve now. The only problem I can see here is to have a good curve fitting. I would suggest B-spline or Bezier curves than polynomial fit. I think I will stop here and leave you to figure out the rest. Hope this helps.

Note that the following script uses Image Processing Toolbox to find the edges of the curves.

clc;clear;close all;

grayImage = imread('2.png');
[rows, columns, numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
    grayImage = grayImage(:, :, 2); % Take green channel.
end

% find edge.
bw = edge(grayImage,'canny');
imshow(bw);

[x, y] = find(bw == 1);
P = [x,y];

% For each point, find a point that is of distance 1 or sqrt(2) to it, i.e.
% find its connectivity.
cP =[0,0]; % add a place holder
for i = 1:length(x)
    px = x(i);
    py = y(i);
    dx = x - px*ones(size(x));
    dy = y - py*ones(size(y));
    distances = (dx.^2 + dy.^2).^0.5;
    c = [find(distances == 1); find(distances == sqrt(2))];
    cP(end+1:end+length(c),:) = [ones(length(c),1)*i, c];
end
cP (1,:) = [];% remove the place holder

% remove duplicates
cP = unique(sort(cP,2),'rows');

% seperating curves
Q{1} = cP(1,:);
for i = 2:length(cP)
    cp = cP(i,:);
    % search for points in cp in Q.
    for j = 1:length(Q)
        check = ismember(cp,Q{j});
        if ~any(check) && j == length(Q) % if neither has been saved in Q
            Q{end+1} = cp;
            break;
        elseif sum(check) == 2 % if both points cp has been saved in Q
            break;
        elseif sum(check) == 1 % if only one of the points exists in Q, add the one missing.
            Q{j} = [Q{j}, cp(~check)];
            break;
        end
    end
    % review sets in Q, merge the ones having common points
    for j = 1:length(Q)-1
        q = Q{j};
        for m = j+1:length(Q)
            check = ismember(q,Q{m});
            if sum(check)>=1 % if there are common points
                Q{m} = [Q{m}, q(~check)]; % merge
                Q{j} = []; % delete the merged set
                break;
            end
        end
    end
    Q = Q(~cellfun('isempty',Q)); % remove empty cells;
end
% each cell in Q represents a curve. Note that points are not ordered.

figure;hold on;axis equal;grid on;
for i = 1:length(Q)
    x_ = x(Q{i});
    y_ = y(Q{i});

    coefficients = polyfit(y_, x_, 3); % Gets coefficients of the    formula.

    % Fit a curve to 500 points in the range that x has.
    fittedX = linspace(min(y_), max(y_), 500);

    % Now get the y values.
    fittedY = polyval(coefficients, fittedX);
    plot(fittedX, fittedY, 'b-', 'linewidth', 4);
    % Overlay the original points in red.
    plot(y_, x_, 'r.', 'LineWidth', 2, 'MarkerSize', 1)

    formula = poly2sym([coefficients(1),coefficients(2),coefficients(3)]);
    % formulaD = vpa(formula)
    df=diff(formula);
    lengthOfCurve(i) = double(int((sqrt(1+df^2)),min(y_),max(y_)));
end

Result:

enter image description here

Anthony
  • 3,595
  • 2
  • 29
  • 38
  • I need to get the function all curves in this image, because i get lenght, i don´t use the hought transform – AlexZ Apr 17 '17 at 02:49
  • OK, I was wrong. Hough Transformation only helps to find linear edges. I have some ideas here but there is a major problem. In your second image, the thicknesses of the curves are not 1 pixel. Is this what we have to deal with or the curves are normally 1 pixel thick? – Anthony Apr 17 '17 at 11:56
  • Sometimes the thickness varies, but in general it will be within the minimum range of pixels – AlexZ Apr 19 '17 at 15:24
  • I was reading about the hough transform and I'm grossly well heard, the detail is that if it is possible for me to get the curve equation, I read that in straight lines if I get the equation, but I have the With the curved lines gives you the same thing, I'm checking your updates in addition to reading bibliography to see if there is a way to do it with the hough transform, in case you could have my code would have been redundant when Hough already gives you most of that. – AlexZ Apr 19 '17 at 15:26
  • My main problem is that I should not use any GUI or toolbox, if I manage to get some progress I will post it here to share my knowledge with you, I am checking your work, thanks :) – AlexZ Apr 19 '17 at 15:28
  • Only the line `bw = edge(grayImage,'canny');` in the code requires _image processing toolbox_. If you can't use toolbox, there are three references at the bottom of [Edge function documentation page](http://uk.mathworks.com/help/images/ref/edge.html) and you can use them to write your own Edge function. – Anthony Apr 19 '17 at 22:39
  • @AlexZ Generalised Hough Transform can be used to find curves. I tried to understand the math behind, but I think I would need at least a full day to crack it, so I will raise the white flag here. I'm looking forward to seeing your progress :). – Anthony Apr 19 '17 at 23:12
  • Ok, thanks, I'll try more and hope to share more, I appreciate your interest – AlexZ Apr 24 '17 at 03:52
  • But I have some questions of your code @Anthony , I have been checking and I do not understand well what you did in update 2, those curves that you painted at the end of red, that are above the blues (you say that the blues you painted them in Matlab ?) If your answer is yes, I do not know if there is a method that passes the lines of an image to a graph ??, my main problem is that I have to extract the function of each curve present in a retina and I am already blank, I thought it was the curve length ... but I do not know if it's okay to do it. – AlexZ Apr 25 '17 at 15:36
  • @AlexZ The codes plot the curves are `plot(fittedX, fittedY, 'b-', 'linewidth', 4); plot(y_, x_, 'r.', 'LineWidth', 2, 'MarkerSize', 1)` They are almost identical to what you had in your question. The blues are the fitted curves and reds are extracted points. `coefficients = polyfit(y_, x_, 3);` is the code calculates the coefficients for curve functions and it is almost identical to yours as well. You get the length of each curve here `lengthOfCurve(i) = double(int((sqrt(1+df^2)),min(y_),max(y_)));`, agained almost identical to yours. – Anthony Apr 25 '17 at 16:26
  • If you read my comment in the code: `% each cell in Q represents a curve. Note that points are not ordered.`, I should probably have made it clearer: `% each cell element in Q is a set of points that represent one of the curves in the input picture.` You can use these point sets to find the best equations for the curves and then generate enough points to calculate the lengths. – Anthony Apr 25 '17 at 16:30
2

You can get a good approximation of the arc lengths using regionprops to estimate the perimeter of each region (i.e. arc) and then dividing that by 2. Here's how you would do this (requires the Image Processing Toolbox):

img = imread('6khWw.png');        % Load sample RGB image
bw = ~imbinarize(rgb2gray(img));  % Convert to grayscale, then binary, then invert it
data = regionprops(bw, 'PixelList', 'Perimeter');  % Get perimeter (and pixel coordinate
                                                   %   list, for plotting later)
lens = [data.Perimeter]./2;  % Compute lengths

imshow(bw)  % Plot image
hold on;
for iLine = 1:numel(data),
  xy = mean(data(iLine).PixelList);  % Get mean of coordinates
  text(xy(1), xy(2), num2str(lens(iLine), '%.2f'), 'Color', 'r');  % Plot text
end

And here's the plot this makes:

enter image description here

As a sanity check, we can use a simple test image to see how good an approximation this gives us:

testImage = zeros(100);        % 100-by-100 image
testImage(5:95, 5) = 1;        % Add a vertical line, 91 pixels long
testImage(5, 10:90) = 1;       % Add a horizontal line, 81 pixels long
testImage(2020:101:6060) = 1;  % Add a diagonal line 41-by-41 pixels
testImage = logical(imdilate(testImage, strel('disk', 1)));  % Thicken lines slightly

Running the above code on this image, we get the following:

enter image description here

As you can see the horizontal and vertical line lengths come out close to what we expect, and the diagonal line is a little bit more than sqrt(2)*41 due to the dilation step extending its length slightly.

gnovice
  • 125,304
  • 15
  • 256
  • 359
0

I try with this post but i don´t understand so much, but the idea Colours123 sounds great, this post talk about GUI https://www.mathworks.com/matlabcentral/fileexchange/24195-gui-utility-to-extract-x--y-data-series-from-matlab-figures

Ztarlight
  • 116
  • 1
  • 10
-1

I think that you should go through the image and ask if there is a '1' if yes, ask the following and thus identify the beginning of a curve, get the length and save it in a BD, I am not very good with the code , But that's my idea

Colours123
  • 606
  • 1
  • 5
  • 14