snibgo's ImageMagick pages

Islands

Suppose we have an arbitrary black shape and an arbitrary black border. The shape is an "island"; it is surrounded by white. The black border extends to the image edges; it is a "mainland".

Easy shapes

Some shapes have a central point, from which straight lines can be drawn to every edge point without crossing the edge. In other words, from the central point we can "see" every edge point.

How do we know if an image qualifies as an "easy shape? We could depolar from an assumed central point, scale this to a single line, then use a simplified version of graph1d.bat to scale it back up again. If the round-trip hasn't changed the image (much), then we have found a central point.

Here is an easy island surrounded by an easy mainland. You can download is_easy.xcf, which has the island in one layer and the mainland in another. See Gimp and IM on how to extract the layers with extrXcfLayers.bat.

if not exist is_easy.xcf copy %PICTBAT%shapeEasy.xcf is_easy.xcf

call %PICTBAT%extrXcfLayers is_easy.xcf

%IM%convert ^
  is_easy_island.png ^
  -background White -layers flatten ^
  -threshold 50%% ^
  is_easy_island.png

is_easy_island.png

is_easy_island.png

is_easy_mainland.png

is_easy_mainland.png

is_easy_flattened.png

is_easy_flattened.png

We can unroll using depolar, do any processing, and invert the distortion.

%IM%convert ^
  -verbose ^
  is_easy_flattened.png ^
  -transparent White ^
  -distort depolar -1 ^
  ( +clone ^
    ( xc:Red -size 9x1 xc:White +append ^
      -write mpr:REDLINES +delete ^
    ) ^
    -fill mpr:REDLINES -draw "color 0,0 reset" ^
  ) ^
  +swap -composite ^
  -write is_easy_depol.png ^
  -distort polar -1 ^
  is_easy_ex.png 
is_easy_depol.png is_easy_ex.png

Instead of drawing red lines, we might pick off boundary points and convert the coordinates back with data from "-verbose". Thus, we can find the correspondence between island points and mainland points.

is_easy_flattened.png PNG 320x200 320x200+0+0 8-bit sRGB 1.71KB 0.000u 0:00.000
DePolar Distort, Internal Coefficents
  c0 = +188.679623
  c1 = +0.000000
  c2 = +160.000000
  c3 = +100.000000
  c4 = -3.141593
  c5 = +3.141593
  c6 = +0.019635
  c7 = +0.943398
DePolar Distort, FX Equivelent:
  -fx 'aa=(i+.5)*0.019635 -3.141593;
       rr=(j+.5)*0.943398 +0.000000;
       xx=rr*sin(aa) +160.000000;
       yy=rr*cos(aa) +100.000000;
       v.p{xx-.5,yy-.5}' \
xc:Red=>Red XC 1x1 1x1+0+0 16-bit sRGB 0.000u 0:00.000
xc:White=>White XC 9x1 9x1+0+0 16-bit sRGB 0.000u 0:00.000
xc:Red=>REDLINES XC 1x1=>10x1 10x1+0+0 16-bit sRGB 0.000u 0:00.000
mpr:REDLINES=>REDLINES XC 1x1=>10x1 10x1+0+0 16-bit sRGB 0.000u 0:00.000
is_easy_flattened.png=>is_easy_depol.png PNG 320x200 320x200+0+0 8-bit sRGB 7.59KB 0.031u 0:00.016
Polar Distort, Internal Coefficents
  c0 = +188.679623
  c1 = +0.000000
  c2 = +160.000000
  c3 = +100.000000
  c4 = -3.141593
  c5 = +3.141593
  c6 = +50.929582
  c7 = +1.059998
Polar Distort, FX Equivelent:
  -fx 'ii=i+page.x-160.000000; jj=j+page.y-100.000000;
       xx=(atan2(ii,jj)-0.000000)/(2*pi);
       xx=xx-round(xx);
       xx=xx*2*pi*50.929582 + v.w/2;
       yy=(hypot(ii,jj)-0.000000)*1.059998;
       v.p{xx-.5,yy-.5}' \
is_easy_flattened.png=>is_easy_ex.png PNG 320x200 320x200+0+0 8-bit sRGB 29.2KB 0.031u 0:00.014

More usefully, we can displace vertically, eg to stretch the island out to the mainland. See Clut cookbook: Cluts from graphics for techniques.

Complex shapes

Some shapes have no central point.

I made this image with Gimp, putting the island in one layer and the border in another. You can download is_src.xcf.

if not exist is_src.xcf copy %PICTBAT%shapeSamp.xcf is_src.xcf

call %PICTBAT%extrXcfLayers is_src.xcf

%IM%convert ^
  is_src_island.png ^
  -background White -layers flatten ^
  -threshold 50%% ^
  is_src_island.png

is_src_island.png

is_src_island.png

is_src_border.png

is_src_border.png

is_src_flattened.png

is_src_flattened.png

Gradient between island and mainland

A "morphology distance" on the flattened image would give us the distance from any land.

For morphing from one shape to another, we need something different: a smooth gradient from the island to the mainland. It should be exactly black where adjacent to the island, and exactly white adjacent to the mainland.

This is not a simple problem. Here is a solution.

Make a gradient that starts black at the island,
and becomes lighter at distances away from the island.

%IM%convert ^
  is_src_island.png ^
  -morphology Distance Euclidean:4,600^! ^
  is_isl_dist.png
is_isl_dist.png

Make a gradient that starts white at the mainland,
and becomes darker at distances away from the mainland.

%IM%convert ^
  is_src_border.png ^
  -morphology Distance Euclidean:4,600^! ^
  -negate ^
  is_bord_dist.png
is_bord_dist.png

Merge the two gradients into one that is black at the island and white at the mainland.

call %PICTBAT%fanComp ^
  is_bord_dist.png ^
  is_isl_dist.png ^
  is_grad1.png

For fanComp.bat, see Composite compositions: Fan compose.

The gradient is quite good,
though it is hard to judge from this image.

is_grad1.png

For clarity, show contours.

%IM%convert ^
  is_grad1.png ^
  ( -size 1x500 gradient: -rotate 90 -duplicate 10 +append ) ^
  -clut ^
  -morphology edgein diamond:1 ^
  is_grad2.png

The contour is linear.
Contours show successive stages of a morph between one shape and the other.

is_grad2.png

Show contours over the flattened image.

%IM%convert ^
  is_src_flattened.png ^
  ( is_grad2.png -alpha set -channel A -evaluate set 50%% ) ^
  -composite ^
  is_grad3.png

The contours are entirely between the island and mainland, as required.

is_grad3.png

Centre line

We find a centre-line between the island and mainland by reducing to a skeleton, and removing line ends. The centre-line will generally be different to the 50% gradient.

See Anthony's Morphology of shapes: Thinning down to a Skeleton.

4-connected skeleton

%IM%convert ^
  is_src_flattened.png ^
  -channel RG ^
  -morphology Erode Diamond ^
  -morphology Thinning:-1 Skeleton:3 +channel ^
  -write is_skel_4con.png ^
  -channel G ^
  -morphology Thinning:-1 LineEnds ^
  is_skel_4con_loops.png
is_skel_4con.png is_skel_4con_loops.png

From the 4-connected skeleton,
make a thinner 8-connected skeleton.

%IM%convert ^
  is_skel_4con.png ^
  -channel RG ^
  -morphology Thinning:-1 Diagonals ^
  -morphology Thinning Corners ^
  -write is_skel_8con.png ^
  -channel G ^
  -morphology Thinning:-1 LineEnds ^
  is_skel_8con_loops.png

Removing these line ends doesn't work.

is_skel_8con.png is_skel_8con_loops.png

From the 4-connected skeleton,
remove line ends and then make a thinner 8-connected skeleton.

%IM%convert ^
  is_skel_4con.png ^
  -channel RG ^
  -morphology Thinning:-1 LineEnds ^
  -morphology Thinning:-1 Diagonals ^
  -morphology Thinning Corners ^
  -morphology Thinning LineEnds:4 ^
  is_skel_8con_loops2.png
is_skel_8con_loops2.png

Octagonal convex hull of the island

See Anthony's Morphology of shapes: ConvexHull.

Very slow.

%IM%convert ^
  -verbose ^
  is_src_island.png ^
  -negate ^
  -channel RG ^
  -morphology Close Diamond ^
  -morphology Thicken:-1 ConvexHull ^
  -morphology Close Diamond ^
  is_convhull.png
is_convhull.png

Very slow.

%IM%convert ^
  -verbose ^
  is_src_island.png ^
  -negate ^
  -channel RG ^
  -morphology Close Square ^
  -morphology Thicken:-1 ConvexHull ^
  -morphology Close Square ^
  is_convhullSq.png
is_convhullSq.png

Very slow.

%IM%convert ^
  -verbose ^
  is_src_island.png ^
  -negate ^
  -channel RG ^
  -morphology Close Disk:1 ^
  -morphology Thicken:-1 ConvexHull ^
  -morphology Close Disk:1 ^
  is_convhullDisk.png
is_convhullDisk.png

Joining island to mainland

Make a line at the edge of the mainland.

%IM%convert ^
  -verbose ^
  is_src_border.png ^
  -morphology EdgeIn Square ^
  is_src_border_edge.png
is_src_border_edge.png

Pick points that are adjacent to the island. For highest precision we would use all the points. Instead, we pick a sample, using nLightest.bat.

%IM%convert ^
  is_src_island.png ^
  -morphology EdgeIn Square ^
  -write is_src_island_edge.png ^
    ( +clone -blur 0x10 -auto-level ) ^
    +swap -compose Multiply -composite ^
  is_src_island_edge_bl.png

The indented lines are optional. They place greater emphasis on high-entropy locations.

is_src_island_edge.png is_src_island_edge_bl.png

We use an exclusion radius of 5% of min(width,height).

set nlDEBUG=1
call %PICTBAT%nLightest is_src_island_edge_bl.png 9999 5 >is_isl_pnts.lis
is_src_island_edge_bl_nl_dbg.png

How many points did nLightest find?

echo nl_nFound=%nl_nFound% 

if "%nl_nFound%"=="" goto :error
if "%nl_nFound%"=="0" goto :error
nl_nFound=72 

nLightest.bat doesn't pick the points in a nice neat clockwise or anticlockwise order. We blurred is_src_island_edge.png before calling nLightest so the more "interesting" locations were found first. (If we wanted the points in order, we could modulate the line by a circular gradient that was centred on a point that was on the island.)

The command above redirected the coordinates to is_isl_pnts.lis:

360,227
491,156
369,246
521,262
508,169
381,229
530,242
125,70
122,115
105,176
348,248
256,97
125,169
117,194
527,68
134,97
93,157
153,317
101,114
179,277
147,279
506,247
145,78
461,113
441,138
425,153
389,253
479,253
132,209
168,295
548,75
110,93
143,227
512,148
291,152
228,161
465,269
134,297
512,83
195,263
97,136
157,260
213,252
408,167
388,176
533,138
135,328
475,96
116,150
307,166
208,174
274,108
280,134
526,181
143,181
522,222
400,278
546,104
352,178
450,284
328,241
421,283
242,113
241,144
188,181
259,239
233,244
330,172
280,237
306,235
164,183
524,201

For each of these points, we can find the nearest point on the border (mainland). How?

It is tempting to find a load of points on the mainland, and find which of these is closest to each island point. But distances will then cross the island.

Instead, we put single black pixel on a white background, and use morphology IterativeDistance, masked by the island, to find distances from that point. Along the edge of the border, the lowest value found is the closest border to the point.

The integer after "IterativeDistance" is, ideally, the maximum expected distance.

To show the process, let us find the closest border point to the coordinate (108,96), which is towards the top-left of the island.

We write intermediate results purely for illustration.

set InX=108
set InY=96
set MAX_DIST=600

for /F "usebackq tokens=1-3 delims=," %%W ^
in (`%IM%convert ^
  is_src_island.png ^
  ^( +clone -negate -write mpr:MASK +delete ^) ^
  -fill #fff -colorize 100 ^
  -fill #000 -draw "point %InX%,%InY%" ^
  -mask mpr:MASK ^
  -morphology IterativeDistance:%MAX_DIST% Euclidean ^
  +mask ^
  -write is_grad_samp.png ^
  is_src_border_edge.png ^
  -compose CopyOpacity -composite ^
  -write is_grad_samp2.png ^
  -background #fff -alpha Remove ^
  -auto-level ^
  -format "%%[fx:minima]," -write info: ^
  +transparent #000 ^
  -write is_grad_samp3.png  ^
  sparse-color:`) ^
do (
  set MIN=%%W
  set blackX=%%X
  set blackY=%%Y
)

echo MIN=%MIN% blackX=%blackX% blackY=%blackY% 
MIN=0 blackX=108 blackY=40 
is_grad_samp.png is_grad_samp2.png is_grad_samp3.png

We can show the result:

%IM%convert ^
  is_src_flattened.png ^
  -fill Red -draw "line %InX%,%InY% %blackX%,%blackY%" ^
  is_grad_samp4.png
MIN=0 blackX=108 blackY=40 
is_grad_samp4.png

We should check that MIN is zero. If it is, we have a valid border point.

The command above finds the match for one point. To find all matches, the script joinIslandMainland.bat loops through the points. The script needs a white island on a black background, so we negate it first.

%IM%convert ^
  is_src_island.png ^
  -negate ^
  is_island_neg.png

set jimDEBUG=1

call %PICTBAT%joinIslandMainland ^
  is_island_neg.png ^
  is_isl_pnts.lis ^
  is_src_border_edge.png ^
  out.csv

%IM%convert %jimDEBUG_FILE% is_join1.png
is_join1.png

Lines don't cross each other, which is good.

Less desirable features:

The first two undesirable features are unavoidable unless the island and mainland are exactly parallel.

We can treat the centre line as a mainland edge, or (with a little initial processing) as an island.

%IM%convert ^
  is_src_flattened.png ^
  -morphology Erode Diamond ^
  -morphology Thinning:-1 Skeleton ^
  -morphology Thinning:-1 "LineEnds:1^>" ^
  is_cent_line.png
is_cent_line.png
set jimDEBUG=1

call %PICTBAT%joinIslandMainland ^
  is_island_neg.png ^
  is_isl_pnts.lis ^
  is_cent_line.png ^
  out.csv


%IM%convert %jimDEBUG_FILE% is_join2.png
is_join2.png

The "less desirable features" have been reduced but not entirely eliminated.

We could iterate the process, dividing each distance into two, generating a (probably) very smooth transition from the island to the mainland. It is probably easier (and quicker?) to use the Gradient method above.

Multiple lakes

This section was written before ImageMagick acquired the "-connected-components" feature. See the official Connected Components Labeling page.

We sometimes have multiple "lakes", ie a number of white shapes on a black background. This example includes five large and three small shapes.

is_multi_lakes.png

We might ask:

We start by reducing each shape to a single white pixel. If a shape was a ring, it would be reduced to multiple pixels.

%IM%convert ^
  is_multi_lakes.png ^
  -morphology Thinning:-1 Skeleton:3 ^
  -morphology Thinning:-1 LineEnds ^
  is_multi_lakes_one.png
is_multi_lakes_one.png

If the command had included "-morphology Erode Diamond", it would have missed the small shapes.

We count the white pixels, to find how many shapes there are:

%IM%convert ^
  is_multi_lakes_one.png ^
  ( +clone -fill Black -colorize 100 ) ^
  -metric AE ^
  -compare -format "%%[distortion]" ^
  info: 
8

We list the white pixels:

%IM%convert ^
  is_multi_lakes_one.png ^
  +transparent White ^
  sparse-color: >is_mlo_list.lis

type is_mlo_list.lis
39,34,graya(255,1) 127,37,graya(255,1) 242,61,graya(255,1) 151,98,graya(255,1) 54,129,graya(255,1) 180,137,graya(255,1) 155,154,graya(255,1) 270,157,graya(255,1) 

Or we list the coordinates of the white pixels to a space-separated file:

call %PICTBAT%listWhiteSps is_multi_lakes_one.png is_mlo_coords.lis
39 34
127 37
242 61
151 98
54 129
180 137
155 154
270 157

We can walk through this list of coordinates. For each one, first find the number of pixels in the shape. Then we find the bounding rectangle.

del is_mlo_walk.lis 2>nul

for /F "usebackq tokens=1,2" %%X in (is_mlo_coords.lis) do (

  %IM%convert ^
    is_multi_lakes.png ^
    ^( +clone ^
       -fill Red -floodfill +%%X+%%Y White ^) ^
    -metric AE ^
    -compare -format "X=%%X,  Y=%%Y,  size=%%[distortion], " ^
    info:  >>is_mlo_walk.lis

  %IM%convert ^
    is_multi_lakes.png ^
    -fill Red -floodfill +%%X+%%Y White ^
    -fill Black +opaque Red ^
    -format " BoundRect=%%@\n" ^
    info:  >>is_mlo_walk.lis
)
type is_mlo_walk.lis
X=39,  Y=34,  size=1919,  BoundRect=52x49+18+14
X=127,  Y=37,  size=1,  BoundRect=1x1+127+37
X=242,  Y=61,  size=8001,  BoundRect=113x103+193+13
X=151,  Y=98,  size=1406,  BoundRect=37x49+136+73
X=54,  Y=129,  size=6559,  BoundRect=97x99+14+78
X=180,  Y=137,  size=2,  BoundRect=2x1+179+137
X=155,  Y=154,  size=4,  BoundRect=2x2+154+153
X=270,  Y=157,  size=2310,  BoundRect=64x51+238+132

"Line Ends" is slow. If the shapes approach the size of the image and there aren't many of them, an alternative may be faster. For example, the following finds a white pixel, flood-fills, and repeats until no more are found. As this needs a loop, we might as well do the other tasks (finding the size and bounding rectangle) at the same time.

del is_mlo_walk2.lis 2>nul

set TMPIMG=%TEMP%\is_temp.png

%IM%convert ^
  is_multi_lakes.png ^
  %TMPIMG%

:loop

set fwX=
for /F "usebackq tokens=1,2 delims=, " %%L in (`%IM%convert ^
  %TMPIMG% ^
  +transparent White ^
  sparse-color:`) do (
  set fwX=%%L
  set fwY=%%M
)

if not "!fwX!"=="" (
  rem echo fwS=!fwS! fwX=!fwX! fwY=!fwY! >>is_mlo_walk2.lis

  %IM%convert ^
    %TMPIMG% ^
    ^( +clone ^
       -fill Red -floodfill +!fwX!+!fwY! White ^
       -write %TMPIMG% ^) ^
    -metric AE ^
    -compare -format "X=!fwX!,  Y=!fwY!,  size=%%[distortion], " ^
    info:  >>is_mlo_walk2.lis

  %IM%convert ^
    is_multi_lakes.png ^
    -fill Red -floodfill +!fwX!+!fwY! White ^
    -fill Black +opaque Red ^
    -format " BoundRect=%%@\n" ^
    info:  >>is_mlo_walk2.lis
)
if not "!fwX!"=="" goto loop

type is_mlo_walk2.lis
X=204,  Y=13,  size=8001,  BoundRect=113x103+193+13
X=30,  Y=14,  size=1919,  BoundRect=52x49+18+14
X=127,  Y=37,  size=1,  BoundRect=1x1+127+37
X=149,  Y=73,  size=1406,  BoundRect=37x49+136+73
X=33,  Y=78,  size=6559,  BoundRect=97x99+14+78
X=279,  Y=132,  size=2310,  BoundRect=64x51+238+132
X=179,  Y=137,  size=2,  BoundRect=2x1+179+137
X=154,  Y=153,  size=4,  BoundRect=2x2+154+153

This has found the same sizes and bounding rectangles. It has found different coordinates but they are, of course, within the shapes.

ASIDE: The commands just given use "sparse-color:" to list just the white pixels, and then pulls out the coordinates of the first one. When there are no white pixels there will be no coordinates, hence the test for !fxW! being blank.

An alternative is to create a single white pixel, and search for it:

for /F "usebackq tokens=1,3,4 delims=()@, " %%L in (`%IM%compare ^
  -metric RMSE ^
  -similarity-threshold 0 ^
  -dissimilarity-threshold 1 ^
  -subimage-search ^
  %TMPIMG% xc:White NULL: 2^>^&1`) do (
  set fwS=%%L
  set fwX=%%M
  set fwY=%%N
)

The test is then whether fwS is zero. If it is, then a white pixel was found. This method is much slower than using "sparse-color:".

NOTE: After the above was written, a new feature was added to ImageMagick: "-connected-components". This is a very fast method for finding patches of connected colours. See the official documentation Connected Components Labeling.

%IM%convert ^
  is_multi_lakes.png ^
  -define connected-components:verbose=true -connected-components 4 ^
  NULL: 
Objects (id: bounding-box centroid area mean-color):
  0: 320x200+0+0 158.2,101.1 43798 srgb(0,0,0)
  1: 113x103+193+13 246.6,65.1 8001 srgb(255,255,255)
  5: 97x99+14+78 59.3,130.2 6559 srgb(255,255,255)
  6: 64x51+238+132 269.3,155.2 2310 srgb(255,255,255)
  2: 52x49+18+14 41.1,35.0 1919 srgb(255,255,255)
  4: 37x49+136+73 152.4,97.1 1406 srgb(255,255,255)
  8: 2x2+154+153 154.5,153.5 4 srgb(255,255,255)
  7: 2x1+179+137 179.5,137.0 2 srgb(255,255,255)
  3: 1x1+127+37 127.0,37.0 1 srgb(255,255,255)

This has found nine components: the eight white shapes, and the black background.

The centroid coordinates may not be within the shape. (For example, the centroid of a crescent-moon shape is not within the crescent moon.) From the results, a crop could be made of the bounding-box, and the crop searched for a pixel of the found colour.

Overlapping shapes

The techniques above are suitable when one shape is entirely within another.

When shapes overlap, the techniques can be adapted by making another shape that contains all the others. This could be a circle, rectangle or union of the smaller shapes. Treat that enclosing shape as the mainland, and each overlapping shape as an island, and continue as above.

Further reading

See also Nearest coastal point and Correlating shapes (not yet published).

Scripts

For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.

joinIslandMainland.bat

rem Given:
rem   %1 island image (white island on black background)
rem   %2 list of coords adjacent to island
rem   %3 mainland edge image (white line on black background)
@rem Writes:
@rem   %4 output CSV file, list of coords on mainland that correspond to island coords
@rem
@rem Also uses:
@rem   jimDEBUG if 1, creates debug image
@rem   jimMAX_DIST
@rem
@rem Updated:
@rem   24-May-2016



@if "%4"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 jim


set ISLAND=%1
set INCOORDS=%2
set MNLAND_EDGE=%3
set OUTCSV=%4

set DEBUG_FILE=%BASENAME%_%sioCODE%_dbg%EXT%
set DBG_LINES=%TEMP%\%~n1_%sioCODE%_dbglns.txt

del %OUTCSV% 2>nul
if "%jimDEBUG%"=="1" del %DBG_LINES% 2>nul

set TMPEXT=.miff
set TMP_ISLAND=%TEMP%\%~n1%TMPEXT%
set TMP_MNLAND_EDGE=%TEMP%\%~n3%TMPEXT%

if "%jimMAX_DIST%"=="" set jimMAX_DIST=100

%IM%convert %ISLAND% %TMP_ISLAND%

%IM%convert %MNLAND_EDGE% %TMP_MNLAND_EDGE%

for /F "tokens=1-2 delims=, " %%X in (%INCOORDS%) do (
  for /F "usebackq tokens=1-3 delims=," %%W in (`%IM%convert ^
    %TMP_ISLAND% ^
    ^( +clone -write mpr:MASK +delete ^) ^
    -fill #fff -colorize 100 ^
    -fill #000 -draw "point %%X,%%Y" ^
    -mask mpr:MASK ^
    -morphology IterativeDistance:%jimMAX_DIST% Euclidean ^
    +mask ^
    %TMP_MNLAND_EDGE% ^
    -compose CopyOpacity -composite ^
    -background #fff -alpha Remove ^
    -channel RGB -auto-level +channel ^
    -format "%%[fx:minima]," -write info: ^
    +transparent #000 ^
  sparse-color:`) do (
    set MIN=%%W
    set blackX=%%X
    set blackY=%%Y
  )

  echo %%X,%%Y  MIN=!MIN! !blackX!,!blackY!

  if !MIN!==0 (
    echo !blackX!,!blackY! >>%OUTCSV%

    if "%jimDEBUG%"=="1" echo line %%X,%%Y !blackX!,!blackY! >>%DBG_LINES%
  )
)

if "%jimDEBUG%"=="1" if exist %DBG_LINES% %IM%convert ^
  %ISLAND% ^
  %MNLAND_EDGE% ^
  -compose Lighten -composite ^
  -stroke Red -draw @%DBG_LINES% ^
  %DEBUG_FILE%

@call echoRestore

@endlocal & set jimDEBUG_FILE=%DEBUG_FILE%

nLightest.bat

@rem From image %1, finds (%2) lightest points.
@rem
@rem After each point is found,
@rem    blacks out pixels within radius (r) of that point
@rem where r = min(width,height) * %3/100
@rem %3 defaults to 10 (percent).
@rem
@rem Returns number of points found. Could be < n, or even zero (?).
@rem
@rem Also uses:
@rem   nlDEBUG if 1, creates debugging image.
@rem   nlPOINTSIZE optional, pointsize for debug images.
@rem   nlSTROKEWIDTH for debug.
@rem   nlONLY_WHITE if 1, doesn't auto-level,
@rem      so finds only points that are exactly white.
@rem
@rem Returns:
@rem   nl_nFound number of points found, integer >= 0
@rem   echos list of coordinates.
@rem
@rem Updated:
@rem   24-May-2016 v7 needs -channel RGB for -auto-level.



@if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 nl

for /F "usebackq" %%L in (`cygpath %TEMP%`) do set CYGTEMP=%%L

@set TMPFILE=%TEMP%\%~n1_%sioCODE%.miff
@set CYGTMPFILE=%CYGTEMP%\%~n1_%sioCODE%.miff
@set LISTFILE=%BASENAME%_%sioCODE%.lis
@set TMPDEBUGFILE=%BASENAME%_%sioCODE%_dbg.miff
@set DEBUGFILE=%BASENAME%_%sioCODE%_dbg%EXT%
@set DEBUGDRAW=%TEMP%\%~n1_%sioCODE%_dbgdrw.txt

del %DEBUGDRAW% 2>nul

if "%nlPOINTSIZE%"=="" set nlPOINTSIZE=20

if "%nlSTROKEWIDTH%"=="" set nlSTROKEWIDTH=1

set MAX_FIND=%2
if "%MAX_FIND%"=="" set MAX_FIND=10

set PC_RAD=%3
if "%PC_RAD%"=="" set PC_RAD=10

set AUTOLEV=-channel RGB -auto-level +channel
if "%nlONLY_WHITE%"=="1" set AUTOLEV=

for /F "usebackq" %%L in (`%IM%convert ^
  -ping ^
  %INFILE% ^
  -format "PIX_RAD=%%[fx:int(min(w,h)*%PC_RAD%/100+0.5)]" ^
  info:`) do set %%L

%IM%convert %INFILE% -colorspace Gray %AUTOLEV% %TMPFILE%

if "%nlDEBUG%"=="1" %IM%convert %INFILE% %TMPDEBUGFILE%

set /A nFound=0

:loop

set MAX=
set whiteX=

for /F "usebackq tokens=1-3 delims=:, " %%W ^
in (`%IMDEV%convert ^
    %TMPFILE% ^
    -process onewhite ^
    NULL: 2^>^&1`) ^
do (
  if "%%W"=="onewhite" (
    set MAX=%%W
    set whiteX=%%X
    set whiteY=%%Y
    set /A whiteX2=%%X+%PIX_RAD%
  )
)

if "!MAX!"=="onewhite" if "!whiteX!" neq "none" (
  %IM%convert ^
    %TMPFILE% ^
    -fill #000 -draw "circle %whiteX% %whiteY% %whiteX2% %whiteY%" ^
    %AUTOLEV% ^
    %TMPFILE%

  if "%nlDEBUG%"=="1" (
    (
      echo fill None stroke #f00 circle %whiteX%,%whiteY%,%whiteX2%,%whiteY%
      echo fill #f00 stroke None text %whiteX%,%whiteY% '!nFound!'
    ) >>%DEBUGDRAW%
  )

  set /A nFound+=1
  echo %whiteX%,%whiteY%
  if !nFound! LSS %MAX_FIND% goto loop
)

rem if "%nlDEBUG%"=="1" %IM%convert %TMPDEBUGFILE% %DEBUGFILE%

if "%nlDEBUG%"=="1" if exist %DEBUGDRAW% %IM%convert ^
  %INFILE% ^
  -strokewidth %nlSTROKEWIDTH% ^
  -pointsize %nlPOINTSIZE% ^
  -draw @%DEBUGDRAW% ^
  %DEBUGFILE%


call echoRestore

@endlocal & set nl_nFound=%nFound%& set nlDEBUG_FILE=%DEBUGFILE%

fanComp.bat

See Composite compositions: Fan compose.

rem A compose like a fan.
@rem
@rem result = src / (src-dest+1)

@if "%3"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@%IM%convert ^
  %1 ^
  %2 ^
  ( -clone 1 -evaluate Divide 2 ) ^
  ( -clone 0-1 ^
    -compose Mathematics ^
      -define compose:args=0,0.5,-0.5,0.5 ^
      -composite ^
  ) ^
  -delete 0-1 ^
  -compose DivideSrc -composite ^
  %3

listWhiteSps.bat

rem From image %1,
rem creates text file %2 with coords of white pixels. (Space between x and y.)
rem Uses sparse-color and DOS tools; fast but messy.

@if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal

call echoOffSave

set BAT_FILE=%TEMP%\%~n1_lws.bat

echo BAT_FILE=%BAT_FILE%

echo call %PICTBAT%sparseCoord.bat ^^>%BAT_FILE%
%IM%convert %1 -alpha set +transparent #fff sparse-color:>>%BAT_FILE%

call %BAT_FILE%>%2

call echoRestore

sparseCoord.bat

:loop
@if "%1"=="" @exit /B 0
@echo %1 %2
@shift
@shift
@shift
@shift
@goto loop

All images on this page were created by the commands shown, using:

%IM%identify -version
Version: ImageMagick 6.9.2-5 Q16 x64 2015-10-31 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Visual C++: 180031101
Features: Cipher DPC Modules OpenMP 
Delegates (built-in): bzlib cairo freetype jng jp2 jpeg lcms lqr openexr pangocairo png ps rsvg tiff webp xml zlib

Source file for this web page is islands.h1. To re-create this web page, execute "procH1 islands".


This page, including the images, is my copyright. Anyone is permitted to use or adapt any of the code, scripts or images for any purpose, including commercial use.

Anyone is permitted to re-publish this page, but only for non-commercial use.

Anyone is permitted to link to this page, including for commercial use.


Page version v1.2 15-July-2014.

Page created 03-Jun-2016 10:54:35.

Copyright © 2016 Alan Gibson.