How to create a centerline between lines in image
2 views (last 30 days)
Show older comments
Dekel Mashiach
on 23 Apr 2022
Commented: Image Analyst
on 23 Apr 2022
Hi,
I'm trying to create a centerline between the two lines. Can someone help me?
[rows, columns] = size(birdsEyeImage);
leftEdges = nan(rows, 1);
rightEdges = nan(rows, 1);
for row = 1 : rows
t = find(birdsEyeImage(row, :), 1, 'first');
if ~isempty(t)
leftEdges(row) = t;
rightEdges(row) = find(birdsEyeImage(row, :), 1, 'last');
end
end
midPoints = ((leftEdges + rightEdges) / 2);
imshow(birdsEyeImage)
hold on
%x = 1:rows;
plot(midPoints, '*')
2 Comments
Matt J
on 23 Apr 2022
I'm trying to create a centerline between the two lines.
I see 4 lines in the image you have posted, two white and two blue.
Accepted Answer
Image Analyst
on 23 Apr 2022
If you want no breaks, then interpolate. Like this. midX and midY are the line coordinates.
% Initialization steps.
clc;
clearvars;
close all;
workspace;
fontSize = 16;
grayImage = imread('2 lines.png');
if ndims(grayImage) == 3
% It's color. Take the red channel.
grayImage = grayImage(:, :, 1);
end
imshow(grayImage, []);
impixelinfo;
axis('on', 'image')
mask = logical(grayImage > 140 & grayImage < 255);
mask = bwareafilt(mask, 2); % Make sure we have only two lines.
mask = bwskel(mask);
imshow(mask);
impixelinfo;
axis('on', 'image')
labeledImage = bwlabel(mask);
line1 = ismember(labeledImage, 1);
line2 = ismember(labeledImage, 2);
% Get rows and columns of each line.
[r1, c1] = find(line1);
[r2, c2] = find(line2);
for k = 1 : length(r1)
distances = sqrt((r1(k) - r2) .^ 2 + (c1(k) - c2) .^ 2);
[minDistance, index] = min(distances);
% Find the midPoint
midX(k) = mean([c1(k), c2(index)]);
midY(k) = mean([r1(k), r2(index)]);
% Burn into mask
mask(round(midY(k)), round(midX(k))) = true;
end
% Need to add a small amount of noise to x to make the values unique.
midX = midX + 0.001 * rand(size(midX));
midY = midY + 0.001 * rand(size(midY));
% Interpolate x and y to make sure there are no gaps.
kVec = 1 : length(midX);
kFit = linspace(1, kVec(end), 10000);
xFit = interp1(kVec, midX, kFit, 'linear');
yFit = interp1(kVec, midY, kFit, 'linear');
% Remove duplicate values
xy = unique(round([xFit(:), yFit(:)]), "rows");
% Extract individual x and y.
midX = xy(:, 1);
midY = xy(:, 2);
hold on;
plot(midX, midY, 'r.', 'MarkerSize', 10);
2 Comments
Image Analyst
on 23 Apr 2022
If it worked, the usual thing to do is to click the "Accept this answer", 🙂 unless you want to wait to see if anyone else will offer a better answer.
More Answers (2)
Image Analyst
on 23 Apr 2022
Edited: Image Analyst
on 23 Apr 2022
Make it easy for us to help you. Attach the actual PNG image file, not a Fig file. Then we can open it with imread() and not have any other stuff surrounding it like tick marks, etc. Just attach the one with white lines, not white and blue lines.
What I'd to is to skeletonize and label the image. Then I'd loop over all pixels in the one line and use sqrt() to get distances to all the other pixels in the other line. Then use min() to get the index of the closest one. Then get the average of the two pixels x and y values and assign that point to be true or use plot() to drop a marker there. Here's a start
mask = logical(imread(filename));
mask = bwareafilt(mask, 2); % Make sure we have only two lines.
mask = bwskel(mask);
imshow(mask);
labeledImage = bwlabel(mask);
line1 = ismember(labeledImage, 1);
line2 = ismember(labeledImage, 2);
% Get rows and columns of each line.
[r1, c1] = find(line1);
[r2, c2] = find(line2);
for k = 1 : length(r1)
distances = sqrt((r1(k) - r2) .^ 2 + (c1(k) - c2) .^ 2);
[minDistance, index] = min(distances);
% Find the midPoint
midX = mean([c1(k), c2(index)]);
midY = mean([r1(k), r2(index)]);
% Burn into mask
mask(round(midY), round(midX)) = true;
% Optionally drop a marker there
hold on;
plot(midX, midY, 'r.', 'MarkerSize', 10);
end
2 Comments
Image Analyst
on 23 Apr 2022
OK, that's a color image, not a binary image. So I need to do a little extra color segmentation to get just the two lines. But I did it for you and here it is:
% Initialization steps.
clc;
clearvars;
close all;
workspace;
fontSize = 16;
grayImage = imread('2 lines.png');
if ndims(grayImage) == 3
% It's color. Take the red channel.
grayImage = grayImage(:, :, 1);
end
imshow(grayImage, []);
impixelinfo;
mask = logical(grayImage > 140 & grayImage < 255);
mask = bwareafilt(mask, 2); % Make sure we have only two lines.
mask = bwskel(mask);
imshow(mask);
labeledImage = bwlabel(mask);
line1 = ismember(labeledImage, 1);
line2 = ismember(labeledImage, 2);
% Get rows and columns of each line.
[r1, c1] = find(line1);
[r2, c2] = find(line2);
for k = 1 : length(r1)
distances = sqrt((r1(k) - r2) .^ 2 + (c1(k) - c2) .^ 2);
[minDistance, index] = min(distances);
% Find the midPoint
midX = mean([c1(k), c2(index)]);
midY = mean([r1(k), r2(index)]);
% Burn into mask
mask(round(midY), round(midX)) = true;
% Optionally drop a marker there
hold on;
plot(midX, midY, 'r.', 'MarkerSize', 10);
end
If you want the line to be unbroken, you can use interp1() to get a value for either every row or every column. Just index your midpoints and call interp1(). I think you can do it, right?
See Also
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!