Clear Filters
Clear Filters

How do I reduce the number of points in a plotted trace?

2 views (last 30 days)
Hello,
I am trying to create an algorithm that converts an image of a black and white tracing into a plot. So far, my code can convert the image into points using the code:
lion = imread('lion.png')
r = 1024;
c = 768;
count = 1;
for i = 1:r
for j = 1:c
if lion(i,j,1) ~= 255
%lion
lion_y(count) = -i + 1000;
lion_x(count) = j;
end
count = count + 1;
end
end
plot(lion_x,-lion_y,'.')
It can successfully convert the png into plotted points (I have attached the png to this posting as well):
--->
But, when zooming in, the line is composed of multiple points to make it appear thicker:
How would I create a trace of these points (perhaps some sort of average?) so that the image can be reduced to a single-point width?
so that the final image would look something like this:
This was made by manually outlining the trace using ginput(). This is not the ideal approach because it is not as accurate and it would be time consuming for processing over 100 images. Is there a way to automate this process?
I tried using the boundry function but it did not capture the details inside the image. It also didn't have a high enough resolution to capture the sharp edges (like in the mane).
Thank you for your help!

Accepted Answer

DGM
DGM on 26 Aug 2021
Edited: DGM on 26 Aug 2021
I don't know of any simple means to convert an image into a minimal line drawing. Features like the intersection of the facial lines with the nose or the intersection of the tail lines with the tuft are structurally the same -- they're a transition between a constant-width line and a solid region. Yet whatever operation we use must somehow recognize that they are to be handled in completely different ways and interpret where the stroke should be. I don't know that basic filtering or morphological operations can do that neatly and consistently.
Something like this might work, but it's going to collapse some solid features.
% read image, convert to a logical image
A = rgb2gray(imread('lion.png'))<127;
% reduce line width
B = bwmorph(A,'thin',Inf);
% display the image
imshow(~B); hold on
This is a little bit better at not collapsing cusps, but it leaves them heavy. That's not much better.
A = rgb2gray(imread('lion.png'))<127;
D = bwdist(bwperim(A,4));
C = (D>4 & D<7) & A;
C = bwmorph(C,'thin',Inf);
C = ~bwareaopen(~C,500,4);
Other methods still have similar issues with thick cusps. This destroys open curves and still leaves small islands.
A = rgb2gray(imread('lion.png'))>127;
[L nb] = bwlabel(A);
st = strel('disk',11);
B = false(size(A));
Bm = false(size(A));
for b = 1:nb
% dilate each blob
thisblob = L == b;
thisblob = imdilate(thisblob,st);
% and trim it against the others
maskedblob = thisblob & ~Bm;
Bm = Bm | thisblob;
maskedblob = bwperim(maskedblob);
B = B | maskedblob;
end
B = bwmorph(B,'thin',Inf);
B = imclearborder(B);
Like I said, I don't know of a good way to do this. Maybe someone else does.
The above examples should give you a line image without any need for finding coordinates. If you really truly want to use plot(), just use find(), since B is already a logical image.
% or if you really want to use xy coordinates
[y x] = find(B);
plot(x,y,'.')
  2 Comments
FsC
FsC on 27 Aug 2021
Hi DGW,
Thank you so much for your response. From what you've said, I can see that each method has trade-offs.
The second and third pictures look good. I tried your code but, got a different result.
Running code for pic 2:
Running code for picture 3:
For pic 2 I made the plot using imshow(~C)
and for pic3 the plot was displayed using imshow(~B)
Did you use a different method to display the images that you got? or did you overlay the ouput of multiple processes?
Thank you for you help!
DGM
DGM on 28 Aug 2021
Edited: DGM on 28 Aug 2021
Your images are both different sizes. The output image should be the same size as the original. I don't know whether that's because your source is a different size or because you're saving the figure.
If it's because your source image was a different size, then you might have to adjust some parameters. Things like the strel diameter in this version depend on the line thickness.
If you're saving the figure, don't do that. Save images using imwrite(). Saving the figure means that the saved image is dependent on the figure geometry and is subject to destructive nearest-neighbor interpolation. It's tantamount to a screenshot. With single-pixel features like those in these images, lines simply disappear, so if that's what's going on, the result will make it hard to tell.
For example, this is the same code running on the web-version with the attached source image:
A = rgb2gray(imread('lion.png'))>127;
[L nb] = bwlabel(A);
st = strel('disk',11);
B = false(size(A));
Bm = false(size(A));
for b = 1:nb
% dilate each blob
thisblob = L == b;
thisblob = imdilate(thisblob,st);
% and trim it against the others
maskedblob = thisblob & ~Bm;
Bm = Bm | thisblob;
maskedblob = bwperim(maskedblob);
B = B | maskedblob;
end
B = bwmorph(B,'thin',Inf);
B = imclearborder(B);
size(A)
ans = 1×2
1140 1346
size(B) % same size as source
ans = 1×2
1140 1346
imshow(~B)
Since the displayed image is subject to nearest-neighbor interpolation and it's been downscaled, most of it disappears. If you saved the figure at this point, that's all you'd get. If you were able to zoom in on it, you'd see what's missing.
clf
imshow(~B(100:400,100:400))
That's just my guess.

Sign in to comment.

More Answers (0)

Categories

Find more on Images in Help Center and File Exchange

Products


Release

R2020a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!