4
$\begingroup$

I'm new to image analysis with Mathematica, so far it looks it has great capabilities. I'm trying to do an image partition, similar to ImagePartition[], but "random". Suppose I have the image below:

myImage =

enter image description here

Now, I can do ImagePartition[myImage, {100,100}], to get sub-images of 100 by 100 pixels:

enter image description here

How could I, instead of this 'orderly' partition of the image, seed some random points in the image, and retrieve the {width, height} sub-images centered on each of the points. For instance, we generate some number of random points on the image:

myRndPts = 
  Table[{RandomInteger[ImageDimensions[myImage][[1]]], 
    RandomInteger[ImageDimensions[myImage][[2]]]}, 800];
Show[myImage, ListPlot[myRndPts,
  PlotStyle -> Directive[Red, PointSize[Large]]]]

enter image description here

Then, from the center of each point, we make a sub-image of the desired {width, height}. In doing so, what to do with points that fall on the edges of the image? Maybe trim the window size, or add some extra 'padding'?

Currently, I'm thinking on doing simply ImagePartition[myImage,{width,height},{dw,dh}], with dw=dh=1, where dw and dh are the offset of the partition. This offset would --I believe-- sample all possible subpartitions of {width, height}. Then, from this list, I could select n random elements. However, this would not be efficient with large images, since it will generate a bunch of images I won't end up using.

Thanks

$\endgroup$
2

3 Answers 3

3
$\begingroup$

For a given image (im), a list of 2D points (pts) and side length (radius), the function rectangleCoords finds the arguments of Rectangles centered at pts and forced to stay within the image rectangle.

We can use rectangleCoords directly with ImageTrim, or further process the output from rectangleCoords to generate row and column specs for ImageTake.

The function imageTake, given the same inputs, computes the parameters $\{row_1, row_2\}$ and $\{col_1, col_2\}$ to get the sub-image that spans $row_1$ to $row_2$ and $col_1$ to $col_2$ using ImageTake[im, {row_1, row_2}, {col_1,col_2}].

ClearAll[rectangleCoords, imageTake]

rectangleCoords[im_, pts_, radius_] := Transpose[
   Transpose @ MapThread[Clip[#, {1, #2}] &] @ 
    {Transpose[pts + # radius/2], ImageDimensions[im]} & /@ {-1, 1}];

imageTake[im_, pts_, radius_] := Module[{rowscols = 
    Map[Apply[{ImageDimensions[im][[2]] + 1 - Reverse @ #, #2} &] @*
       Reverse @* Transpose] @ rectangleCoords[im, pts, radius]}, 
  ImageTake[im, ##] & @@@ rowscols ]

Example:

myIm = ExampleData[{"TestImage", "Lena"}];

SeedRandom[1]
myPts = RandomSample[Tuples @ Range @ ImageDimensions @ myIm, 40];

myRadius = 60; 

Show[myIm, 
 Graphics[{PointSize[Large], Red, Point @ myPts, 
   EdgeForm[Blue], FaceForm[], 
   Rectangle @@@ rectangleCoords[myIm, myPts, myRadius]}],
  PlotRange -> All]

enter image description here

ImageTrim[myIm, rectangleCoords[myIm, myPts, myRadius]] // 
  Multicolumn[#, 8] &

enter image description here

imageTake[myIm, myPts, myRadius] // Multicolumn[#, 8] &

enter image description here

$\endgroup$
3
$\begingroup$

This is kind of fun - may not be what you want, however.

i = ExampleData[{"TestImage", "House"}]
ImageDimensions@i
(* {256, 256} *)

Create a VoronoiMesh and use those polygons as a mask.

pts = RandomReal[{20, 236}, {10, 2}];
mp = MeshPrimitives[VoronoiMesh[pts], 2];
Show[i, Epilog -> {FaceForm[None], EdgeForm[Thick], mp}]

enter image description here

RemoveBackground@ImageCrop@
ImageSubtract[i, Show[i, Graphics[{#}, PlotRange -> 256]]] & /@ mp

enter image description here

$\endgroup$
1
  • $\begingroup$ This is cool, maybe I'll use this for another application, haha. But yeah, I'm just trying to generate a bunch of squared sub-images, so unfortunately the Voronoi tesselation won't do the trick here. But this is cool. $\endgroup$ Commented Mar 8, 2021 at 2:17
2
$\begingroup$

Okay, in case it is of any use to somebody, following the comment by @C.E. I took a look into ImageTrim[]. It was only a matter of finding out the specific format of the arguments of the function.

(*Load image*)
myIm = Import["https://i.sstatic.net/5f46D.jpg"];
(*Generate some random points*)
myPts = Table[{RandomInteger[ImageDimensions[myIm][[1]]], 
        RandomInteger[ImageDimensions[myIm][[2]]]}, 100];
(*Radius from each point, to make a square*)
myRadius = 10;
(*This generates the coordinates of each sub-image*)
mySubIms = Table[
       {{myPts[[i]][[1]] - (myRadius - 1), 
         myPts[[i]][[2]] - (myRadius - 1)}, 
        {myPts[[i]][[1]] + (myRadius - 1), 
         myPts[[i]][[2]] + (myRadius - 1)}},
       {i, 1, Length[myPts]}];
(*This is the list of all sub-images*)
mySubImages = ImageTrim[myIm, mySubIms]; 

(*We can visualize the overlayed sub-images*)

    Show[myIm,
     Table[Graphics[{FaceForm[None], 
        EdgeForm[Directive[Dashed, Thickness[0.005], Blue]],
        Rectangle[mySubImages[[i]][[1]], mySubImages[[i]][[2]]]}],
      {i, 1, Length[mySubImages]}]]

enter image description here

As you can see, it seems that the sub-images on the edges are simply trimmed, which for my application is fine. Maybe adding some padding would be preferred for some other applications.

Feel free to improve or make this method more efficient. I wrote things step by step, for clarity, but maybe there are some performance improvements if needed for large images.

$\endgroup$

Your Answer

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

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