4
$\begingroup$

This post is somewhat connected to my past post "how to get a deformable mesh from this binarized image" although not quite important for what I intend to ask here.

I have an image of an animal tissue and have done some segmentation procedure on the image to get a mask (kindly see below):

image of cell

enter image description here

mask after segmentation

enter image description here

Highlighted[img,imgmask] shows that the segmentation is not good enough.

enter image description here

Since the segmentation is imperfect, I have devised a crude strategy to "delete edges" where incorrect segmentation occurs and furthermore, "add edges" where segmentation does not result in a boundary.

I have tweaked the code from my previous post and am posting it here so that both functions yield a mask at the end with an added or removed edge.

Edge Removal

(* function for removing edges *)
deleteEdgeSegments[origimage_, initmask_, selectedpts_,
color_: RGBColor[0.2, 0.5, 0.9]] := 
Module[{pts = selectedpts, original = origimage, mask = initmask },

pts = Map[FromDigits, pts, {2}];
mask = DeleteSmallComponents[ReplacePixelValue[mask, pts -> 0]];

If[False,Print@GraphicsRow[{HighlightImage[
HighlightImage[original, initmask], {color,
AbsolutePointSize[10], Point@pts}], 
HighlightImage[original, mask]}, ImageSize -> Full]];

Return@mask;]

similarly the function for adding edges is given below:

Edge Addition

(* code for edge addition *)
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 I wish to implement a Controls related scheme to integrate the disjointed pieces of code together.

Specifically, I wish to import an image and its associated mask so that a user may perform an "Edge Deletion" and "Edge Addition" operations on the mask - with a list of points selected using MousePosition from HighlightedImage[image,mask].

If the mask comes out to be incorrect/defective (checked by HighlightedImage[image,newmask]) after a single operation (add or delete edge) then the user can undo the change on the mask i.e. the old mask stays. However, if the resulting mask is reasonable after the operation the user can replace the old mask with the newmask.

The user can do operations for as many times as necessary to get a mask that fits correctly when superimposed on the image.

The question is how to implement such a Control for the process. I have not worked with Controls before.

I am pasting a schematic that I have drawn to elucidate the process.

enter image description here

$\endgroup$
  • $\begingroup$ thanks for notifying me. I upvoted all the answers to all the previous questions. Sorry i am relatively a newcomer so did not have an idea about accepting answer. I did that too. $\endgroup$ – Ali Hashmi Oct 24 '16 at 18:27
4
$\begingroup$

I have been working on the problem for some days and came up with a naive/modest strategy to the answer. I am new to DynamicModule so it may not be the perfect solution, however the current code has all the functionality that I desire. The current code is posted below the bold heading titled code for implementing control

There is one problem I want someone to help me with and for that you have my infinite gratitude. the function zoomMask is somewhat slow and moreover, one has to move the mouse very subtly. The function basically enables the user to zoom into the mask (red lines) and select the right pixels for edge deletion or edge addition. Currently the function uses HighlightImage to superimpose the MousePosition on the imagemask. Is there a way to make it faster? Kindly check the image on the right of pixel val -> 0 which delineates the result of zoomMask and the associated code below.

the code can be run by correctSegmentation[image, corresponding mask,"some path to save"]

enter image description here

implementing Control (new code)

correctSegmentation[image_?ImageQ, correspondingMask_?ImageQ, saveDir_?StringQ] := 

Module[{img = image, imgmask = correspondingMask, saveLink = saveDir},

DynamicModule[{list = {}, temp, mouse, mask = imgmask, 
reset = imgmask, (placeholder = 
  HighlightImage[img, Dynamic@mask]), boxwidth = 10, 
interp = 0.07, magmask = 5, rule, undo = imgmask, magimage = 10, 
save},

Framed[Column@{EventHandler[
   Column[{Row@{Magnify[Dynamic@placeholder, magimage],
       Manipulate[
        If[temp =!= None,
         Row@{               
           Style["pixel val" -> PixelValue[mask, Round@temp], 
            Bold, FontSize -> 18, FontFamily -> "Courier"], 
           zoomMask[
            Sequence @@ Flatten@{temp, mask, boxwidth, magmask}]
           }, "Magnify Mask"
         ], {pt, Dynamic[temp = MousePosition["Graphics"]], 
         Locator, Appearance -> None}]
       }, Dynamic@list}],

   {"MouseClicked", 
     1} :> (list = 
      If[(mouse = MousePosition["Graphics"]) =!= None , 
       AppendTo[list, mouse], list])],

  ButtonBar[{
    "Delete Edge" :> 
     If[Length@list > 0, (undo = mask; 
       mask = deleteEdgeSegments[mask, list]; list = {};)],
    "Add Edge" :> 
     If[Length@list > 0, (undo = mask; 
       mask = ReplacePixelValue[mask, 
           interpPoints[list, interp] -> 1] // Pruning // 
         Thinning; list = {};)],
    "Undo Operation" :> (mask = undo; list = {};),
    "Reset Mask" :> (mask = reset; list = {}; undo = mask),
    "Show/Hide points" :> 
     If[Length@list > 
       0, (placeholder = 
        placeholder /. {(rule = 
            HighlightImage[img, Dynamic@mask] -> 
             HighlightImage[
              HighlightImage[img, 
               Dynamic@mask], {RGBColor[0.2, 0.6, 0.9], 
               AbsolutePointSize[1], Point@list}]), 
          Reverse@rule})],
    "Reset points" :> (list = {};),
    "Save" :> (Export[saveLink, mask]; list = {};)
    }, Background -> LightBlue, ImageMargins -> 5]
  }, Background -> LightGray]
]
]

below is the code for zoomMask which is called in the GUI Code

zoomMask code

zoomMask := Function[{x, y, image, width, magnifymask},

Module[{row1, row2, col1, col2, imagedim = ImageDimensions[image]},

CompoundExpression[
If[imagedim[[2]] - y - width <= 0, row1 = 0, 
 row1 = imagedim[[2]] - y - width]; 
If[imagedim[[2]] - y + width >= imagedim[[2]], 
 row2 = imagedim[[2]], row2 = imagedim[[2]] - y + width]; 
If[x - width <= 0, col1 = 0, col1 = x - width];
If[x + width >= imagedim[[1]], col2 = imagedim[[1]], 
 col2 = x + width];
Magnify[ImageTake[HighlightImage[image, {Red, AbsolutePointSize[3], 
    Graphics@Point[Round@{x, y} - 1]}], {row1, row2},{col1,col2}],magnifymask]]
]
]

the other two functions are deleteEdgeSegments and interpPoints. The two codes are slightly modified from the ones posted in the question.

deleteEdgeSegments code (for deleting edges)

deleteEdgeSegments[initmask_, selectedpts_] :=
Module[{pts = selectedpts, mask = initmask, whitepixpos, nearest},

whitepixpos = PixelValuePositions[mask, 1];

nearest = Nearest[whitepixpos, DistanceFunction -> EuclideanDistance];

pts = pts /. patt : {_, _} /; ! MemberQ[whitepixpos, patt] :>(nearest[patt])/. 
x : {{_Integer, _Integer}} :> First@x ;

mask = DeleteSmallComponents[ReplacePixelValue[mask, pts -> 0]];
Return@mask;
] /; Length@selectedpts > 0;

interpPoints code (used for adding edges)

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

whitepixpos = PixelValuePositions[mask, 1];
nearest = Nearest[whitepixpos, DistanceFunction -> EuclideanDistance];
extractends = {First@pts, Last@pts};

MapThread[
 Replace[extractends[[#1]], 
   x_ /; ! MemberQ[whitepixpos, x] :> (#2[pts, 
      First@nearest[x]])] &, {{1, 2}, {PrependTo, AppendTo}}]; 

pts = Partition[DeleteDuplicates@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;

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

] /; Length@selectedpts > 0;
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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