Generating uniform random points over a binary image
Update
Silvia proposed a much faster algorithm that I believe produces I uniform distribution.
Here is my implementation of it.
pointsInMask2[mask_Image, n_Integer, range : {_, _} : {0, 1/2}] :=
Reverse @ ImageData @ Binarize[mask, range]\[Transpose] //
SparseArray[#]["NonzeroPositions"] & //
RandomChoice[#, n] + RandomReal[{-1, 0}, {n, 2}] &
img = Import["http://i.stack.imgur.com/yoPNX.png"];
Graphics[{
AbsolutePointSize[1], Opacity[0.3],
pointsInMask2[img, 75000] // Point
}]
The optional range parameter specifies the range of values in the mask image that are valid target area.
Graphics[{
AbsolutePointSize[1], Opacity[0.3],
pointsInMask2[img, 75000, {1/2, 1}] // Point
}]
A "brute force" method
Although my first method will be superior if one wishes to generate many points there is a more direct and simple approach, though it does not benefit from any of the optimizations in the linked question. That is simply to generate random points and select the ones for which the matching ImageValue
is closer to black than white. This requires redundant sampling and will be increasingly slow on images with a small percentage of black pixels. (For now I am simply estimating the number of samples needed and not performing a check to make sure the requested number are actually produced; if this method proves useful I shall refine my approach.)
pointsInMask[mask_Image, n_Integer] :=
Module[{dims, pts, n2},
dims = ImageDimensions[mask];
n2 = ⌈ 1.1 n #/(# - ImageData[mask] ~Total~ 2) &[Times @@ dims] ⌉;
pts = RandomReal[#, n2] & /@ dims // Transpose;
Select[pts, ImageValue[mask, #] < 0.5 &, n]
]
Test:
mask = Binarize @ Import["http://i.stack.imgur.com/yoPNX.png"]
pts = pointsInMask[mask, 5000];
Graphics[{AbsolutePointSize[1], Point @ pts}]
Following up on my comment and borrowing a method from Vectorizing an image like "Trace Bitmap" in Inkscape:
mask = Binarize @ Import["http://i.stack.imgur.com/yoPNX.png"];
{row, col} = ImageDimensions[mask];
intf = ListInterpolation @ Reverse @ ImageData @ mask;
region = DiscretizeGraphics @
RegionPlot[intf[c, r] < 1/2, {r, 1, row}, {c, 1, col},
PlotPoints -> {row, col}, MaxRecursion -> 0]
You can then apply whichever of the methods from How to generate random points in a region? that you find appropriate, e.g.:
pts = randomFromRegion[region, 15000];
Graphics[{AbsolutePointSize[1], Point @ pts}]
In version 11, one can use ImageMesh[]
in tandem with RandomPoint[]
, like so:
imsh = ImageMesh[ColorNegate[Import["http://i.stack.imgur.com/yoPNX.png"]]];
Graphics[Point[RandomPoint[imsh, 5000]]]