6
$\begingroup$

This question is linked to previous question but significantly different:

I am using Mathematica 10.2. I have a binarized image (see below) and wish to get a deformable mesh from it where I can add lines, delete lines or move nodes that are connecting edges (see the bottom most image). The point is to discretize the white lines into segments (mesh white lines in a way) that can be moved, removed or edited so as to ensure edge correction while not affecting the overall topology. Is that even possible? Any strategy or solution will be appreciated.

enter image description here

Same image as above but with the alterations that I wish to possibly make.

enter image description here

$\endgroup$
2
  • $\begingroup$ even if it is not deformable, what would be a convenient way to add or delete edges? $\endgroup$
    – Ali Hashmi
    Oct 18, 2016 at 11:27
  • $\begingroup$ Can you use Mathematica's drawing tools to manually draw on the image and erase lines (not sure if you can do this to only erase the white lines without erasing the black background)? $\endgroup$
    – user13999
    Oct 21, 2016 at 18:57

2 Answers 2

9
$\begingroup$

UPDATE 2

As I found in this answer, using MorphologicalTransform instead of ImageFilter gives performance gain and also works correctly in latest versions of Mathematica.

Also it is worth to note that the method suggested in the previous section gives false operations in situations like this (note the central part of the image):

img = Import["https://i.stack.imgur.com/2a2j6.png"];

Image[#, Magnification -> 8] & /@ {i = ImageTake[img, {493, 505}, {10, 20}],
  MorphologicalTransform[i, If[#[[2, 2]] == 1 && Total[#, 2] == 3, 1, 0] &]}

output

This problem can be solved by applying Thinning first:

Image[#, Magnification -> 8] & /@ {i = Thinning@ImageTake[img, {493, 505}, {10, 20}],
  MorphologicalTransform[i, If[#[[2, 2]] == 1 && Total[#, 2] == 3, 1, 0] &]}

output

Also (if desirable) it is possible to avoid removal of the end points by slight modification of the filtering function:

Image[#, Magnification -> 8] & /@ {i = Thinning@ImageTake[img, {493, 505}, {10, 20}],
  MorphologicalTransform[i, If[#[[2, 2]] == 1 && Total[#, 2] <= 3, 1, 0] &]}

output


UPDATE

The method of deleting the branch points in the original answer was quite crude, here is correct and more efficient approach based on ImageFilter. Unfortunately it doesn't work in the most recent versions of Mathematica due to a bug introduced in version 9. But with version 8.0.4 it works nicely:

img = Pruning[Thinning[Binarize@ImageCrop@Import["https://i.stack.imgur.com/qYhEr.png"]], 
   1];
img2 = ImageFilter[If[#[[2, 2]] == 1 && Total[#, 2] == 3, 1, 0] &, img, 1];
edges = MorphologicalComponents[img2];
edges // Colorize

image

Here is a workaround which allows to use this method with recent (buggy) versions of ImageFilter:

img2 = ImageFilter[If[#[[3, 3]] == 1 && Total[#[[2 ;; -2, 2 ;; -2]], 2] == 3, 1, 0] &, img, 2]

Original answer

(Not a complete answer but it could be a start.)

Here is how I would approach such a task. I'll show only an interactive approach for deleting and adding edges, but it can be extended for deformation of the edges (requires more work though).

First of all, I find the branch points:

img = Pruning[Thinning[Binarize@ImageCrop@Import["https://i.stack.imgur.com/qYhEr.png"]], 
   1];
branchPoints = 
  ImageValuePositions[MorphologicalTransform[img, "SkeletonBranchPoints"], White];

Now I delete them from the image and highlight the edges:

img2 = HighlightImage[img, {Opacity[1], Black, Disk[#, 1.6] & /@ branchPoints}];
edges = MorphologicalComponents[img2];
edges // Colorize

image

Then I transform it into a Graphics object:

Graphics[GeometricTransformation[{RandomColor[], Point[Position[edges, #]]} & /@ 
   Range[Max[edges]], {{0, 1}, {-1, 0}}], ImageSize -> 800]

image

I can easily select any edge by double-clicking on it:

screenshot

... and then delete it by pressing Del:

screeenshot

Then it is easy to get the indices of the remaining edges back:

screenshot

You also can draw new edges using the Freehand line tool of the Drawing Tools palette:

screenshot

The line drawn can be extracted by applying Cases[#, edge_Line :> First@edge, Infinity] & (but be aware that you'll need to convert obtained coordinates into indices using the inverse of the GeometricTransformation used above):

screenshot

Of course this approach can be extended further using Dynamic functionality which allows interactive manipulations with the data.

$\endgroup$
5
  • $\begingroup$ Thanks a lot. I have it working for the moment without the deformation though (which is extraneous if addition and deletion works). but once i am done implementing a Controls object (GUI) for my approach, I will definitely extend yours. Its wonderful so many thanks! Btw currently i am having trouble with implementing Controls since its my first time implementing a GUI so would really appreciate your help if you are interested and have time. $\endgroup$
    – Ali Hashmi
    Oct 23, 2016 at 18:02
  • $\begingroup$ @AliHashmi I'm glad it helps you. I've updated the answer with a simple edge drawing method based on the Drawing Tools palette. $\endgroup$ Oct 23, 2016 at 18:19
  • $\begingroup$ interesting. have to read up a little bit on these functions. But appears that they do the job :) $\endgroup$
    – Ali Hashmi
    Oct 23, 2016 at 18:31
  • $\begingroup$ @AliHashmi I've updated the answer with better method of deleting the branch points. At the bottom of the update I give a workaround allowing to use this method with the recent (buggy) version of ImageFilter. $\endgroup$ Oct 23, 2016 at 21:14
  • $\begingroup$ i just posted a new question. would be great if you can provide me some feedback : mathematica.stackexchange.com/questions/147943/… $\endgroup$
    – Ali Hashmi
    Jun 8, 2017 at 22:41
1
$\begingroup$

I thought about a scheme to perform two most important operations on the image mask i.e. delete edges (in over-segmented regions or incorrect edges) or add edges (in under-segmented regions) with a list of specified points.

the image below is a mask which we save as imgmask

enter image description here

Addition of Edge Scheme (through interpolation)

lets select some points on the boundaries for adding edges

seq = {{"555", "170"}, {"554", "157"}, {"554", "146"}, {"554", 
"132"}, {"546", "123"}, {"535", "117"}, {"527", "104"}, {"525", 
"95"}, {"521", "88"}, {"520", "82"}, {"513", "81"}};


interpPoints[selectedpts_, interpthresh_] := Module[{pts = selectedpts,
ptsToChange, positionReversal,reverselist, xrange, yrange, xs, ys, ascendListOrder,
interpolatedList, threshold = interpthresh},

pts = Map[FromDigits, pts, {2}];
pts = Partition[pts, 2, 1];
positionReversal = Position[pts, {{x_, _}, {x_, _}}];
reverselist = pts /. t : {{x_, _}, {x_, _}} :> Reverse /@ t;
ascendListOrder = Map[SortBy[#, First] &, reverselist];

interpolatedList = Map[Function[{x},
xs = x[[All, 1]];
ys = x[[All, 2]];

xrange = Range[Sequence @@ xs, threshold];
yrange = Subdivide[Sequence @@ ys, Length[xrange] - 1];
ptsToChange = Thread@{xrange, yrange}
  ], ascendListOrder] // Round;

Flatten[MapAt[Map[Reverse, #] &, interpolatedList, positionReversal], 1]
]

now applying our construct:

pts = Map[FromDigits, seq, {2}];

GraphicsRow[{imgmask, HighlightImage[imgmask, Point@pts],
ReplacePixelValue[imgmask, interpPoints[seq, 0.07] -> 1] //
Thinning // DeleteSmallComponents}, ImageSize -> Full]

enter image description here

Delete edges scheme

lets select some points on the boundaries for removing edges

selectedpts = {{"654", "564"}, {"690", "563"}, {"775", "341"}, {"678",
 "474"}, {"714", "485"}};

deleteEdgeSegments[imagemask_, selectedpts_,color_: RGBColor[0.2, 0.5, 0.9]] := 
Module[{pts = selectedpts, mask = imagemask },
pts = Map[FromDigits, pts, {2}];
GraphicsRow[{HighlightImage[mask, {color, AbsolutePointSize[10], Point@pts}], 
mask = DeleteSmallComponents[ReplacePixelValue[mask, pts -> 0]]},ImageSize -> Full]
]

now applying our construct for deleting edges:

deleteEdgeSegments[imgmask, selectedpts]

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.