snibgo's ImageMagick pages

Mending broken lines

Three methods are shown.

An image may contain broken lines. It might be a scan from an old book, or the result of processing a photograph, or a hand-drawn sketch. A variety of methods can identify and fix these breaks.

Sample inputs

We will consider only white lines on black backrounds. They may be aliased. We assume the image is fully opaque.

We create source images to demonstrate techniques. Each image contains broken lines, with a large gap or a small gap. We will repair the small gaps, leaving the large gaps unchanged. In the first four images, we will join line-ends together.

%IMG7%magick ^
  -size 200x200 xc:Black ^
  -stroke White -strokewidth 5 ^
  -draw "line 0,50 80,50" ^
  -draw "line 120,50 199,50" ^
  -draw "line 0,150 95,150" ^
  -draw "line 105,150 199,150" ^
  mbl_src1.png
mbl_src1.png
%IMG7%magick ^
  -size 200x200 xc:Black ^
  -stroke White -strokewidth 5 ^
  -draw "line 0,50 80,50" ^
  -draw "line 120,60 199,60" ^
  -draw "line 0,150 95,150" ^
  -draw "line 105,160 199,160" ^
  mbl_src2.png
mbl_src2.png
%IMG7%magick ^
  -size 200x200 xc:Black ^
  -stroke White -strokewidth 5 ^
  -draw "line 0,5 80,50" ^
  -draw "line 120,50 199,5" ^
  -draw "line 0,105 95,150" ^
  -draw "line 105,150 199,105" ^
  mbl_src3.png
mbl_src3.png
%IMG7%magick ^
  -size 200x200 xc:Black ^
  -stroke White -strokewidth 5 ^
  -fill None ^
  -draw "circle 100,-20 100,50" ^
  -draw "circle 100,-20 100,150" ^
  -stroke None -fill Black ^
  -draw "rectangle 80,45 120,55" ^
  -draw "rectangle 95,145 105,155" ^
  mbl_src4.png
mbl_src4.png

The following contains broken T-junctions. We will mend the small gap.

%IMG7%magick ^
  -size 200x200 xc:Black ^
  -stroke White -strokewidth 5 ^
  -draw "line 0,5 80,50" ^
  -draw "line 0,105 95,150" ^
  -draw "circle 1000,150 105,150" ^
  mbl_src5.png
mbl_src5.png

ASIDE: The methods shown will join lines from line-ends. When two lines are in close proximity, but line-ends are distant, the methods will not join them.

Such cases can be joined by "-morphology Close", though this will also join line-ends.

%IMG7%magick ^
  -size 200x200 xc:Black ^
  -stroke White -strokewidth 5 ^
  -draw "circle -1000,100 90,100" ^
  -draw "circle 1000,100 110,100" ^
  mbl_src6.png
mbl_src6.png
%IMG7%magick ^
  mbl_src6.png ^
  -morphology Close disk:10 ^
  mbl_src6_close.png
mbl_src6_close.png

The methods

The methods use skeletonized versions of the inputs, and coordinates of the line-ends of the skeletons.

%IMG7%magick ^
  mbl_src1.png ^
  -virtual-pixel Edge ^
  -morphology Thinning:-1 Skeleton ^
  +write mbl_skel1.png ^
  -morphology HMT LineEnds ^
  +write mbl_ends1.png ^
  -transparent Black ^
  sparse-color:mbl_ends1.lis

sed -e 's/ /\n/g' mbl_ends1.lis 
80,48,graya(255,1)
120,48,graya(255,1)
76,50,graya(25.0004%,1)
124,50,graya(74.9996%,1)
80,52,graya(74.9996%,1)
120,52,graya(24.9989%,1)
121,52,graya(25.0004%,1)
120,53,graya(25.0004%,1)
95,148,graya(255,1)
105,148,graya(255,1)
91,150,graya(25.0004%,1)
109,150,graya(74.9996%,1)
95,152,graya(74.9996%,1)
105,152,graya(24.9989%,1)
106,152,graya(25.0004%,1)
105,153,graya(25.0004%,1)
mbl_skel1.png mbl_ends1.png

We can get an approximate line width by dividing the mean lightness of the input image by the mean lightness of the skeleton image.

for /F "usebackq" %%L in (`%IMG7%magick ^
  mbl_src1.png ^
  -format "MN_MAIN=%%[fx:mean]" ^
  info:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick ^
  mbl_skel1.png ^
  -format "MN_SKEL=%%[fx:mean]" ^
  info:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "LINE_THK=%%[fx:%MN_MAIN%/%MN_SKEL%]\nLINE_THK_INT=%%[fx:int(%MN_MAIN%/%MN_SKEL%+0.5)]" ^
  xc:`) do set %%L

echo LINE_THK=%LINE_THK% LINE_THK_INT=%LINE_THK_INT% 
LINE_THK=6.46157 LINE_THK_INT=6 

Skeletonized lines generally contain short stub segments, where one end is a line-end and the other end is either bounded by a junction, or is also a line-end. In this example, each line has split ends, which has doubled the numbers of ends. The script pruneStubs.bat removes these, leaving what was the junction as the new line-end. (For more details, see the Pruning skeleton stubs page.)

%IMG7%magick ^
  mbl_src1.png ^
  -virtual-pixel Edge ^
  -morphology Thinning:-1 Skeleton ^
  mbl_skelp1.png

call %PICTBAT%pruneStubs ^
  mbl_skelp1.png mbl_skelp2.png %LINE_THK_INT%

%IMG7%magick ^
  mbl_skelp2.png ^
  -morphology HMT LineEnds ^
  +write mbl_endsp1.png ^
  -transparent Black ^
  sparse-color:mbl_endsp1.lis

sed -e 's/ /\n/g' mbl_endsp1.lis 
120,48,graya(255,1)
76,50,graya(25.0004%,1)
79,50,graya(255,1)
124,50,graya(74.9996%,1)
120,52,graya(24.9989%,1)
121,52,graya(25.0004%,1)
120,53,graya(25.0004%,1)
105,148,graya(255,1)
91,150,graya(25.0004%,1)
94,150,graya(255,1)
109,150,graya(74.9996%,1)
105,152,graya(24.9989%,1)
106,152,graya(25.0004%,1)
105,153,graya(25.0004%,1)
mbl_skelp1.png mbl_skelp2.png mbl_endsp1.png

We make a script skelPS.bat that makes a skeleton, prunes stubs, and finds the ends.

call %PICTBAT%skelPS ^
  mbl_src1.png mbl_1.png %LINE_THK_INT%
79,50,graya(255,1)
122,50,graya(255,1)
94,150,graya(255,1)
107,150,graya(255,1)
mbl_1.png mbl_1_ends.png
call %PICTBAT%skelPS ^
  mbl_src2.png mbl_2.png %LINE_THK_INT%
79,50,graya(255,1)
122,60,graya(255,1)
94,150,graya(255,1)
107,160,graya(255,1)
mbl_2.png mbl_2_ends.png
call %PICTBAT%skelPS ^
  mbl_src3.png mbl_3.png %LINE_THK_INT%
78,49,graya(255,1)
122,49,graya(255,1)
93,149,graya(255,1)
107,149,graya(255,1)
mbl_3.png mbl_3_ends.png
call %PICTBAT%skelPS ^
  mbl_src4.png mbl_4.png %LINE_THK_INT%
79,49,graya(255,1)
121,49,graya(255,1)
93,150,graya(255,1)
108,150,graya(255,1)
mbl_4.png mbl_4_ends.png
call %PICTBAT%skelPS ^
  mbl_src5.png mbl_5.png %LINE_THK_INT%
78,49,graya(255,1)
93,149,graya(255,1)
mbl_5.png mbl_5_ends.png

Distance between line-ends

Two of the methods join lines-ends that are within a threshold distance of each other. How do we find the pairs of line-ends that are to be joined?

From the above, we have a list of coordinates of line-ends. We can easily calculate the distance between any two line-ends:

distancei,j = hypot (Xi-Xj,Yi-Yj)

... where i,j range from zero to (N-1), where N is the number of line-ends. Then we are interested only in the pairs where the distance is less than or equal to a given threshold. In our worked examples, we will use a proximity threshold of 20 pixels.

set PROX_THRESH=20

The script pointsDist.bat finds those pairs of points, writing the output in a format usable by -draw "@file.txt".

call %PICTBAT%pointsDist mbl_3_ends.csv mbl_3_lines.lis %PROX_THRESH%
line 93,149 107,149

This can be done in a shell script, however it takes O(N2) time. If we have millions of line-ends, this is a problem.

Two methods reduce the problem:

  1. Ignore line-ends that are not close to any other line-end.
  2. Loop through the line-ends in a compiled language.

Method 1 might use this algorithm, using the line-ends image:

  1. For every line-end, make a copy image that has this line-end painted black, and all pixels beyond the threshold distance painted black.
  2. Combine all the results from (1), taking the lightest pixels from each.
  3. The result is white where a line-end is close to another line-end; otherwise black.

Although this algorithm is not O(N2), it is O(N*W*H), so is likely to be just as slow in practice.

An improved algorithm is:

This improved algorithm is O(r2*N), where r is the "radius". It can use either the image of the line-ends (to find line-ends that are near each other), or the initial input image (to find line-ends that are near lines).

This implemented as pointsRdx.bat. The script has a large overhead per line-end, but is massively quicker then pointsDist.bat when we have hundreds of line-ends.

The essential difference between the scripts is:

Method: proximity of line-ends

Join line-ends that are in close proximity. Basic method: find distance between every pair of points. Better: first, eliminate points that are not in proximity to any others. Do it in C.

When we know which pairs of line-ends are close enough to be joined, we can join them with a simple thin white line.

call %PICTBAT%skelPS ^
  mbl_src1.png mbl_jle1.png %LINE_THK_INT%
if ERRORLEVEL 1 goto error

call %PICTBAT%pointsDist ^
  %spsCSVFILE% mbl_jle1_lines.scr %PROX_THRESH%
if ERRORLEVEL 1 goto error

%IMG7%magick ^
  mbl_src3.png ^
  +antialias ^
  -stroke White ^
  -draw "@%pdOUTFILE%" ^
  mbl_jle1r.png
mbl_jle1r.png

As we know the average stroke-width of the input image, we can use that as the width of the added line.

%IMG7%magick ^
  mbl_src3.png ^
  +antialias ^
  -stroke White ^
  -strokewidth %LINE_THK_INT% ^
  -draw "@%pdOUTFILE%" ^
  mbl_jle1rw.png
mbl_jle1rw.png

We put this into a script, proxLineEnds.bat.

call %PICTBAT%proxLineEnds ^
  mbl_src1.png mbl_ple_1.png %PROX_THRESH%
mbl_ple_1.png
call %PICTBAT%proxLineEnds ^
  mbl_src2.png mbl_ple_2.png %PROX_THRESH%
mbl_ple_2.png
call %PICTBAT%proxLineEnds ^
  mbl_src3.png mbl_ple_3.png %PROX_THRESH%
mbl_ple_3.png
call %PICTBAT%proxLineEnds ^
  mbl_src4.png mbl_ple_4.png %PROX_THRESH%
mbl_ple_4.png
call %PICTBAT%proxLineEnds ^
  mbl_src5.png mbl_ple_5.png %PROX_THRESH%
mbl_ple_5.png

Note that this method, like the others, doesn't notice that a pair of line-ends may be the two ends of the same short line, and hence already joined.

Method: extend lines

We can "grow" lines. That is, we can extend them. One method is to erase (make transparent) background pixels around line-ends, then fill in these holes. These extensions can be used as a mask, so an extension is used only where another line-end (or another extension) falls within this exension.

We could operate on one line-end at a time, extending it by a certain radius. We would then compare the results pair-wise. Where two results have white pixels in the same location, then we know the intersection is caused by an extension. This would raise a difficult problem: How do we know how far to extend lines? We find that two extensions intersect, but we don't want either line to be extended beyond the intersection.

Intead, we erase (make transparent) all the black pixels that are within a circle centered at the mid-point between line-ends. Then we fill all the holes, in priority order. The radius of the circles should be half the proximity threshold, and the window_radius is set to the estimated line thickness.

set /A RAD=%PROX_THRESH%/2

%IMDEV%convert ^
  mbl_src3.png ^
  +write mpr:IMG ^
  +antialias ^
  ( mpr:IMG ^
    -fill White -colorize 100 ^
    -fill Black ^
    -draw "translate 100,49 circle 0,0 0,%RAD%" ^
    -draw "translate 100,149 circle 0,0 0,%RAD%" ^
    mpr:IMG -compose Lighten -composite ^
    -fill Black +opaque White ^
  ) ^
  -alpha off -compose CopyOpacity -composite ^
  +write mbl_3t.png ^
  -process 'fillholespri window_radius %LINE_THK_INT% lsr 10%% auto_limit_search off cp window' ^
  mbl_3m.png
mbl_3t.png mbl_3m.png

We put this into a script, extLineEnds.bat.

call %PICTBAT%extLineEnds ^
  mbl_src1.png mbl_ele_1.png %PROX_THRESH%
mbl_ele_1.png
call %PICTBAT%extLineEnds ^
  mbl_src2.png mbl_ele_2.png %PROX_THRESH%
mbl_ele_2.png
call %PICTBAT%extLineEnds ^
  mbl_src3.png mbl_ele_3.png %PROX_THRESH%
mbl_ele_3.png
call %PICTBAT%extLineEnds ^
  mbl_src4.png mbl_ele_4.png %PROX_THRESH%
mbl_ele_4.png
call %PICTBAT%extLineEnds ^
  mbl_src5.png mbl_ele_5.png %PROX_THRESH%
mbl_ele_5.png

Instead of erasing a circle, we could erase a square or other shape. This could improve performance when we want to mend large breaks where the broken ends point directly towards each other.

The script extLineEnds.bat does the job, but doesn't recognise when it has failed. In the second example above, it has extended a pair of parallel lines, and they don't intersect.

Similarly, if the two lines approached each other at a small angle, nearly parallel, the intersection would fall outside the circle (or square). So the lines would be correctly extended, but they wouldn't meet. The process could be repeated until they did meet.

Creating holes then filling them has a problem. The cloning might come from any pixels that are within the search radius. Ideally, we would clone only from the relevant lines.

The script could be modified to skeletonize the result and test whether any line-ends were present in the relevant crops of the result. If they were, it could roll-back those crops, so lines that don't intersect are not extended.

[[We can detect when this situation has occurred: find any line-ends in the area defined by the circle (or square) of the result. If there are two or more line-ends, the situation has probably occurred, so repeat the process with these new line-ends.

Of course, if the lines are exactly parallel, there will be no intersection. When the lines are extended to the edge of the image, it will have no line-end there.]]

Method: T-junctions by coordinates

The methods above join line-ends that are close to each other. We can also handle situations where a line-end is close to another line, but not necessarily an end of that line.

For each line-end:

  1. Crop a square of the required "radius", centred on that line-end.
  2. In the crop, flood-fill from the centre, turning white into black, and find the white pixel that is nearest to the centre. (See Nearest coastal point.)
  3. If there are no white pixels, do no more with this crop.
  4. Otherwise, we need to draw a line on the input image between the line-end and the point that corresponds to the found white pixel.

The flood-fill is needed to prevent a line-end from trivially joining with itself, or the next point along the line, etc. However, when a line ends with a U-shape, we might want the end to join with the pixel on the other branch of the 'U'. If the other branch is connected to the line-end within the crop, this algorithm won't join with it.

For example, for the source image #5, the line-end at (93,149):

set LE_X=93
set LE_Y=149

set /A CRP_WH=2*%PROX_THRESH%+1
set /A CRP_L=%LE_X%-%PROX_THRESH%
set /A CRP_T=%LE_Y%-%PROX_THRESH%

%IMG7%magick ^
  mbl_src5.png ^
  -fill Black +opaque White ^
  -crop %CRP_WH%x%CRP_WH%+%CRP_L%+%CRP_T% +repage ^
  +write mbl_5crp.png ^
  -draw "color %PROX_THRESH%,%PROX_THRESH% floodfill" ^
  mbl_5crpw.png

set ncSEA_COL=black
call %PICTBAT%nearCoast2 mbl_5crpw.png 50%%%% 50%%%%
if ERRORLEVEL 1 exit /B 1
set ncSEA_COL=

echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% 
echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% 
ncCST_X=30 ncCST_Y=20 
mbl_5crp.png mbl_5crpw.png
for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "ML_X=%%[fx:int(%CRP_L%+%ncCST_X%+0.5)]\nML_Y=%%[fx:int(%CRP_T%+%ncCST_Y%+0.5)]" ^
  xc:`) do set %%L

echo ML_X=%ML_X% ML_Y=%ML_Y% 
ML_X=103 ML_Y=149 
%IMG7%magick ^
  mbl_src5.png ^
  +antialias ^
  -stroke White ^
  -draw "line %LE_X%,%LE_Y% %ML_X%,%ML_Y%" ^
  mbl_tcoord.png
mbl_tcoord.png

As before, we can use an appropriate strokewidth:

%IMG7%magick ^
  mbl_src5.png ^
  +antialias ^
  -stroke White ^
  -strokewidth %LINE_THK_INT% ^
  -draw "line %LE_X%,%LE_Y% %ML_X%,%ML_Y%" ^
  mbl_tcoordw.png
mbl_tcoordw.png

We put this into a script, TjuncLineEnds.bat that processes all the line-ends in this manner. The script is more complex than the worked example, as it has to cope with crops that are near the image edge. (I would like IM to have an option so crops can take virtual-pixels.)

call %PICTBAT%TjuncLineEnds ^
  mbl_src1.png mbl_tjle_1.png %PROX_THRESH%
mbl_tjle_1.png
call %PICTBAT%TjuncLineEnds ^
  mbl_src2.png mbl_tjle_2.png %PROX_THRESH%
mbl_tjle_2.png
call %PICTBAT%TjuncLineEnds ^
  mbl_src3.png mbl_tjle_3.png %PROX_THRESH%
mbl_tjle_3.png
call %PICTBAT%TjuncLineEnds ^
  mbl_src4.png mbl_tjle_4.png %PROX_THRESH%
mbl_tjle_4.png
call %PICTBAT%TjuncLineEnds ^
  mbl_src5.png mbl_tjle_5.png %PROX_THRESH%
mbl_tjle_5.png

In the first four examples, the script has added two lines, because the line that is close to a line-end has, itself, a line-end. So a line is added from a line-end to the second line, then another line is added from the second line-end back to the first line. To prevent this from happening, first process the image with a method that joins line-ends together.

If the unbroken line doesn't have a line-end here, the added line segment will be perpendicular to the unbroken line, more or less. We might instead use Hough lines, within just the crop, to identify the intersection point (with the caution that the intersection might be outside the crop).

If we want to close gaps at the edge of the image, we can add a white border, run TjuncLineEnds.bat, then shave off the added border.

FUTURE: TjuncLineEnds.bat is painfully slow. It probably needs a cut-down version of nearCoast.bat.

Combining methods

The script combLineEnds.bat combines some of the above methods. They are run in sequence: the output of one method is used as the input to the next, recalculating line-ends for each method. This simple script uses the same threshold for each method. In practice, different thresholds may be preferred.

call %PICTBAT%combLineEnds ^
  mbl_src1.png mbl_cle_1.png %PROX_THRESH%
mbl_cle_1.png
call %PICTBAT%combLineEnds ^
  mbl_src2.png mbl_cle_2.png %PROX_THRESH%
mbl_cle_2.png
call %PICTBAT%combLineEnds ^
  mbl_src3.png mbl_cle_3.png %PROX_THRESH%
mbl_cle_3.png
call %PICTBAT%combLineEnds ^
  mbl_src4.png mbl_cle_4.png %PROX_THRESH%
mbl_cle_4.png
call %PICTBAT%combLineEnds ^
  mbl_src5.png mbl_cle_5.png %PROX_THRESH%
mbl_cle_5.png

The "Z" in the second example is caused by extLineEnds.bat creating an overshoot, then proxLineEnds.bat joining them.

Real-world example

We use an image from the Canny edge detection page.

set RW_SRC=ca_lab.png
ca_lab.png
%IMG7%magick ^
  %RW_SRC% ^
  -virtual-pixel Edge ^
  -morphology Thinning:-1 Skeleton ^
  mbl_rw1.png

call %PICTBAT%pruneStubs ^
  mbl_rw1.png mbl_skelrw.png %LINE_THK_INT%

%IMG7%magick ^
  mbl_skelrw.png ^
  -morphology HMT LineEnds ^
  +write mbl_endsrw.png ^
  -transparent Black ^
  sparse-color:mbl_endsrw.lis

sed -e 's/ /\n/g' mbl_endsrw.lis 
mbl_rw1.png mbl_skelrw.png mbl_endsrw.png

There are 260 line-ends.

Calculate the approximate width:

for /F "usebackq" %%L in (`%IMG7%magick ^
  %RW_SRC% ^
  -format "MN_MAIN=%%[fx:mean]" ^
  info:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick ^
  mbl_skelrw.png ^
  -format "MN_SKEL=%%[fx:mean]" ^
  info:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "LINE_THK=%%[fx:%MN_MAIN%/%MN_SKEL%]\nLINE_THK_INT=%%[fx:int(%MN_MAIN%/%MN_SKEL%+0.5)]" ^
  xc:`) do set %%L

echo LINE_THK=%LINE_THK% LINE_THK_INT=%LINE_THK_INT% 
LINE_THK=1.12627 LINE_THK_INT=1 

FIXME: we need width before the prune.

call %PICTBAT%combLineEnds ^
  %RW_SRC% mbl_rw_cle.png 10
mbl_rw_cle.png

A blink comparison shows the added lines

%IMG7%magick ^
  -delay 50 ^
  %RW_SRC% ^
  mbl_rw_cle.png ^
  mbl_rw_blnk.gif
mbl_rw_blnk.gif

Method: morphology close

For some purposes, we want to simplify the image, and the original lines are unimportant. Then a simple (and fast) command may be appropriate. Unlike the methods shown above, -morphology close will merge parallel lines that are less then 2*r apart. This may be good or bad, depending on the application.

%IMG7%magick ^
  %RW_SRC% ^
  -morphology close disk:10 ^
  mbl_morphcl.png
mbl_morphcl.png

A blink comparison shows the difference.

%IMG7%magick ^
  -delay 50 ^
  %RW_SRC% ^
  mbl_morphcl.png ^
  mbl_morphcl_blnk.gif
mbl_morphcl_blnk.gif

The image mbl_morphcl.png can be regarded as a thick partition boundary mask.

We often want to simplify this to a skeleton:

%IMG7%magick ^
  mbl_morphcl.png ^
  -morphology Thinning:-1 Skeleton  ^
  mbl_morphclsk.png
mbl_morphclsk.png

A blink comparison shows the difference.

%IMG7%magick ^
  -delay 50 ^
  %RW_SRC% ^
  mbl_morphclsk.png ^
  mbl_morphclsk_blnk.gif
mbl_morphclsk_blnk.gif

From the skeleton, we can make a partition-boundary mask:

call %PICTBAT%pruneStubsRep ^
  mbl_morphclsk.png mbl_partbnd.png all
mbl_partbnd.png

Future

When a number of line-ends are in close proximity to each other, the methods will join them with new lines from every line-end to every other line-end, which is more than necessary and looks ugly. For example, five points that are close together will generate 12 lines joining them. This could be solved by re-calculating line-ends after every join, but performance would be terrible. Perhaps we could extract crops, add each line to the crop, and re-calculate just for the crop.

A method for mending a broken T-junction by extending the line should be possible.

I would like a method for the minimal joining of lines that are in close proximity, as shown in the Aside above. Of course, we can "-morphology close" and skeletonize, but how do we get an "H" shape?

What size of gap should be closed? This is a parameter the user must supply, and the same number is used in all parts of the image. There is scope for automatically determining this number.

Conclusion

Three methods for mending broken lines have been shown. They can be combined, but not with the same line-end data. Instead, they should be run in sequence: the output of one method can be used as the input to another, recalculating line-ends.

Scripts

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

pruneStubs.bat

rem From %1, image of skeletonized white lines on black background, fully opaque,
rem makes output %2 version with short stubs removed.
rem %3 threshold for stub removal, integer number of pixels, or "all".
rem   Stubs larger than this will not be removed.
@rem
@rem Updated:
@rem   15-Feb-2017 added limit so exclusion string doesn't break line length limit.
@rem   6-August-2022 for IM v7
@rem


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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 ps

echo %0: %1 %2 %3

if not "%2"=="" if not "%2"=="." set OUTFILE=%2

set MAX_SIZE=%3
if "%MAX_SIZE%"=="." set MAX_SIZE=
if "%MAX_SIZE%"=="" set MAX_SIZE=10

set DO_ALL=0
if /I "%MAX_SIZE%" EQU "all" set DO_ALL=1

set EXT=.miff
set ENDS=%BASENAME%_ends_ps%EXT%
set SEGS=%BASENAME%_segs_ps%EXT%
set ENDSEGS=%BASENAME%_endsegs_ps%EXT%
set SEGSLIST=%BASENAME%_graysegs_ps.lis
set ENDSLIST=%BASENAME%_ends_ps.lis
set FLOODLIST=%BASENAME%_flood_ps.lis

for /F "usebackq" %%L in (`%IMG7%magick ^
  xc: -format "QNUM=%%q" info:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "CANVAS=%%P%%O" %INFILE%`) do set %%L

%IMG7%magick ^
  %INFILE% ^
-fill White +opaque Black ^
  +repage ^
  ( -clone 0 ^
    -morphology HMT LineEnds ^
    -alpha off ^
    +write %ENDS% ^
    +delete ^
  ) ^
  -define morphology:compose=Darken ^
  -morphology Thinning "LineJunctions;LineJunctions:3>;LineJunctions:5" -clamp ^
  -alpha off ^
  %SEGS%

if ERRORLEVEL 1 exit /B 1

set /A N_DILATE=2*%MAX_SIZE%

set nPruned=0

if %DO_ALL%==1 (

  (
    for /F "usebackq tokens=1-2 delims=, " %%X in (`%IMG7%magick ^
      %ENDS% ^
      -transparent Black ^
      sparse-color:- ^| sed -e 's/ /\n/g' `) do @(
      @echo color %%X,%%Y floodfill
      @set /A nPruned+=1
    )
  ) >%FLOODLIST%

  type %FLOODLIST%

rem       -morphology thicken "3>:-,0,-,1,-,1,0,1,0" 
rem       -morphology thicken "3>:-,0,0,1,-,0,0,1,-" 

  %IMG7%magick ^
    %INFILE% ^
    -alpha off ^
    ^( +clone ^
       -define "morphology:compose=Darken" ^
       -morphology Thinning "LineJunctions;LineJunctions:3>;LineJunctions:5" -clamp ^
      -alpha off ^
       ^( +clone ^
          -fill Black -draw @%FLOODLIST% ^
       ^) ^
       -compose Difference -composite ^
       -alpha off ^
       -negate ^
    ^) ^
    -compose Darken -composite ^
    ^( +clone ^
       -morphology Hit-and-Miss "3x3:0,0,0,0,1,0,0,0,0" ^
       -alpha off ^
       -negate ^
    ^) ^
    -compose Darken -composite ^
    -compose Over ^
    -alpha off ^
    %OUTFILE%

  goto end
)

%IMG7%magick ^
  %SEGS% -negate +write mpr:MASK +delete ^
  %ENDS% ^
  -write-mask mpr:MASK ^
  -morphology Dilate:%N_DILATE% Square ^
  +write-mask ^
  -alpha off ^
  +write %ENDSEGS% ^
  -define "connected-components:verbose=true" ^
  -connected-components 8 ^
  NULL: >%SEGSLIST%

if ERRORLEVEL 1 exit /B 1

:: Building the string: 1000 is okay; 2200 breaks line length.

set HAS_ANY=0
set CH_COLS=
set CH_EXCL=
set nFnd=0
set nExcl=0
for /F "skip=1 tokens=1-5 delims=: " %%A in (%SEGSLIST%) do (
  set HAS_ANY=1
  set /A nFnd+=1

  set ID=%%A
  set BND_BOX=%%B
  set CENTROID=%%C
  set AREA=%%D
  set COL=%%E

  if !AREA! GTR %MAX_SIZE% if !nExcl! LSS 1000 (
    set CH_EXCL=!CH_EXCL!,!ID!
    set /A nExcl+=1
  )
)

echo %0: SEGSLIST=%SEGSLIST% nFnd=%nFnd% nExcl=%nExcl%

if %HAS_ANY%==0 (
  echo %0: ** No components?? **
  exit /B 1
)

:: remove leading comma
set CH_EXCL=%CH_EXCL:~1%
rem echo CH_EXCL=%CH_EXCL%

:: FIXME? not necessarily an error.
if "%CH_EXCL%"=="" exit /B 1

%IMG7%magick ^
  %ENDSEGS% ^
  -define "connected-components:remove=%CH_EXCL%" ^
  -define "connected-components:mean-color=true" ^
  -connected-components 8 ^
  -background Black -layers Flatten ^
  -fill White +opaque Black ^
  -alpha off ^
  -negate ^
  %INFILE% ^
  +repage ^
  -compose Darken -composite ^
  ( +clone ^
    -morphology Hit-and-Miss "3x3:0,0,0,0,1,0,0,0,0" ^
    -alpha off ^
    -negate ^
  ) ^
  -compose Darken -composite ^
  -compose Over ^
  -repage %CANVAS% ^
  -alpha off ^
  %OUTFILE%

if ERRORLEVEL 1 exit /B 1

set /A nPruned=%nFnd%-%nExcl%


:end

echo %0 end: outfile=%OUTFILE%

call echoRestore

endlocal & set psOUTFILE=%OUTFILE%& set psNPruned=%nPruned%

skelPS.bat

rem From %1, white lines on black background, fully opaque,
rem makes output %2 skeletonised with short stubs removed.
@rem
@rem If the given output, %2, is XYZ.png,
@rem creates files:
@rem   XYZ.png       skeletonised image, pruned.
@rem   XYZ_ends.png  just the line-ends
@rem   XYZ_ends.csv  list of the line-ends.
@rem
@rem Updated:
@rem   6-August-2022 for IM v7
@rem


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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 sps

echo %0: %1 %2

if not "%2"=="" if not "%2"=="." set OUTFILE=%2

for /F %%F in ("%OUTFILE%") do (
  set OUTBASE=%%~dpnF
  set OUTEXT=%%~xF
)

set TEMPEXT=.png
set TEMP_SKEL=%OUTBASE%_sps_temp%TEMPEXT%
set OUT_ENDS_CSV=%OUTBASE%_ends.csv
set OUT_ENDS=%OUTBASE%_ends%OUTEXT%

%IMG7%magick ^
  %INFILE% ^
  -threshold 50%% ^
  -virtual-pixel Edge ^
  -morphology Thinning:-1 Skeleton -clamp ^
  -alpha off ^
  %TEMP_SKEL%

if ERRORLEVEL 1 exit /B 1

:: Calculate approx line width.

for /F "usebackq" %%L in (`%IMG7%magick ^
   %INFILE% ^
  -alpha off ^
  -format "MN_MAIN=%%[fx:mean]" ^
  info:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick ^
  %TEMP_SKEL% ^
  -format "MN_SKEL=%%[fx:mean]" ^
  info:`) do set %%L

if "%MN_SKEL%"=="0" (
  echo %0: skeleton [%TEMP_SKEL%] has zero mean
  exit /B 1
)

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "LINE_THK=%%[fx:%MN_MAIN%/%MN_SKEL%]\nLINE_THK_INT=%%[fx:int(%MN_MAIN%/%MN_SKEL%+0.5)]" ^
  xc:`) do set %%L

echo %0: LINE_THK=%LINE_THK% LINE_THK_INT=%LINE_THK_INT%

call %PICTBAT%pruneStubs %TEMP_SKEL% %OUTFILE% %LINE_THK_INT%
if ERRORLEVEL 1 exit /B 1

%IMG7%magick ^
  %OUTFILE% ^
  -morphology HMT LineEnds ^
  -alpha off ^
  +write %OUT_ENDS% ^
  -transparent Black ^
  sparse-color: |sed -e 's/ /\n/g' >%OUT_ENDS_CSV%

echo %0 end: outfile=%OUTFILE%

call echoRestore

endlocal & set spsOUTFILE=%OUTFILE%& set spsCSVFILE=%OUT_ENDS_CSV%& set spsENDS=%OUT_ENDS%& set spsLINE_THK_INT=%LINE_THK_INT%

pointsDist.bat

rem Given %1 is text file containing integer coordinates separated by comma and optional spaces,
rem   one point per line,
rem outputs lines to text file %2 (can be same name as %1)
rem %3 is a floating-point poximity threshold,
rem   finds all pairs of points that are less than or equal to the given threshold.

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

@setlocal enabledelayedexpansion

@call echoOffSave


set INFILE=%1

if not exist %INFILE% exit /B 1



set OUTFILE=%2
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=pd.lis


set GAP_LIMIT=%3
if "%GAP_LIMIT%"=="." set GAP_LIMIT=
if "%GAP_LIMIT%"=="" set GAP_LIMIT=10

rem type %INFILE%

set II=0
for /F "tokens=1,2 delims=, " %%X in (%INFILE%) do (
  rem echo %%X,%%Y
  set lineEnd[!II!].x=%%X
  set lineEnd[!II!].y=%%Y
  set /A II+=1
)

set /A numPnts=%II%
set /A lastEnd=%II%-1
set /A lastEndm1=%II%-2

rem set lineEnd

(for /L %%A in (0,1,%lastEnd%m1) do (
  set nA=%%A
  set /A X0=lineEnd[!nA!].x
  set /A Y0=lineEnd[!nA!].y
  set /A firstB=%%A+1

  for /L %%B in (!firstB!,1,%lastEnd%) do (
    set nB=%%B
    set /A X1=lineEnd[!nB!].x
    set /A Y1=lineEnd[!nB!].y

    rem echo !X0!,!Y0!, !X1!,!Y1!

    for /F "usebackq" %%L in (`%IM%identify ^
      -format "DO_IT=%%[fx:hypot(!X1!-!X0!,!Y1!-!Y0!)<=%GAP_LIMIT%?1:0]" ^
      xc:`) do set %%L

    if !DO_IT!==1 echo line !X0!,!Y0! !X1!,!Y1!
  )
)) >%OUTFILE%

type %OUTFILE%


call echoRestore

endlocal & set pdOUTFILE=%OUTFILE%& set pdNUM_PNTS=%numPnts%

pointsRdx.bat

rem Given %1 is white points on black background,
rem %2 is CSV text file of those coordinates,
rem outputs %3 text file containing pairs of points that are close.
rem %4 is proximity threshold.
@rem
@rem Output file may be empty (returns prdxNUM_PNTS=0).
@rem
@rem Updated:
@rem   6-August-2022 for IM v7
@rem

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

@setlocal enabledelayedexpansion

@call echoOffSave


set INFILE=%1

if not exist %INFILE% exit /B 1


set CSVFILE=%2

if not exist %CSVFILE% exit /B 1

set OUTFILE=%3
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=prdx.lis

set GAP_LIMIT=%4
if "%GAP_LIMIT%"=="." set GAP_LIMIT=
if "%GAP_LIMIT%"=="" set GAP_LIMIT=10

set CRP_FILE=prdxCrop.miff
set OTHERS_CSV=prdxCrop.csv
set OTHERS_CSV2=prdxCrop2.csv

set TEMP_IN=prdx.miff

%IMG7%magick %INFILE% %TEMP_IN%
if ERRORLEVEL 1 exit /B 1

set /A CWH=2*%GAP_LIMIT%+1

set II=0
(for /F "tokens=1,2 delims=, " %%X in (%CSVFILE%) do (
  rem echo %%X,%%Y
  set CW=%CWH%
  set CH=%CWH%
  set CX=%GAP_LIMIT%
  set CY=%GAP_LIMIT%
  set /A XL=%%X-%GAP_LIMIT%
  set /A YT=%%Y-%GAP_LIMIT%
  if !XL! LSS 0 (
    set /A CW+=!XL!
    set /A CX+=!XL!
    set XL=0
  )
  if !YT! LSS 0 (
    set /A CH+=!YT!
    set /A CY+=!YT!
    set YT=0
  )

  for /F "usebackq" %%L in (`%IMG7%magick ^
    %TEMP_IN% ^
    -crop !CW!x!CH!+!XL!+!YT! +repage ^
    -fill Black ^
    -draw "color !CX!,!CY! point" ^
    -format "MAX=%%[fx:maxima]" ^
    +write info:^
    -transparent Black ^
    sparse-color:%OTHERS_CSV%`) do set %%L

  if not !MAX!==0 (
    rem echo %%X,%%Y

    rem %IMG7%magick %CRP_FILE% -transparent Black sparse-color:|sed -e 's/ /\n/g' >%OTHERS_CSV%

    sed -e 's/ /\n/g' %OTHERS_CSV% >%OTHERS_CSV2%

    for /F "tokens=1,2 delims=, " %%A in (%OTHERS_CSV2%) do (
      set /A X1=!XL!+%%A
      set /A Y1=!YT!+%%B
      set DOIT=1
      if !Y1! LSS %%Y set DOIT=0
      if !Y1! EQU %%Y if !X1! LSS %%X set DOIT=0
      if !X1! EQU %%X if !Y1! EQU %%Y set DOIT=0
      if !DOIT!==1 (
        for /F "usebackq" %%L in (`%IMG7%magick identify ^
          -format "DOIT=%%[fx:hypot(!X1!-%%X,!Y1!-%%Y)<=%GAP_LIMIT%?1:0]" ^
          xc:`) do set %%L
      )
      if !DOIT!==1 (
        echo line %%X,%%Y,!X1!,!Y1!

        set /A II+=1
      )
    )
  )
)
) >%OUTFILE%

:: Note: When a line is short, its two line-ends are close, so will be joined.

echo %0: II=%II%
call echoRestore

endlocal & set prdxOUTFILE=%OUTFILE%& set prdxNUM_PNTS=%numPnts%

proxLineEnds.bat

rem From image of white lines on black background,
rem writes output %2
rem joining line-ends that are within proximity threshold %3.
@rem
@rem Also uses:
@rem   skelLineEnds if 1, skeletonises the output.
@rem   threshLineEnds if 1, thresholds the output at 50%.
@rem
@rem Updated:
@rem   6-August-2022 for IM v7
@rem


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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 ple

if not "%2"=="" if not "%2"=="." set OUTFILE=%2

set PROX_THRESH=%3
if "%PROX_THRESH%"=="" set PROX_THRESH=.

call %PICTBAT%skelPS ^
  %INFILE% %BASENAME%_%sioCODE%.png
if ERRORLEVEL 1 exit /B 1

call %PICTBAT%pointsRdx ^
  %spsENDS% ^
  %spsCSVFILE% %BASENAME%_lines_%sioCODE%.scr %PROX_THRESH%
if ERRORLEVEL 1 exit /B 1

echo %0: prdxOUTFILE=%prdxOUTFILE%

if "%skelLineEnds%"=="1" (
  set sSKEL=-morphology Thinning:-1 skeleton -clamp
) else (
  set sSKEL=
)

if "%threshLineEnds%"=="1" (
  set sTHRESH=-threshold 50%%
) else (
  set sTHRESH=
)

%IMG7%magick ^
  %INFILE% ^
  +antialias ^
  -stroke White ^
  -strokewidth %spsLINE_THK_INT% ^
  -draw "@%prdxOUTFILE%" ^
  %sSKEL% %sTHRESH% ^
  %OUTFILE%
if ERRORLEVEL 1 exit /B 1

call echoRestore

@endlocal & set %sioCODE%OUTFILE=%OUTFILE%

extLineEnds.bat

rem From image of white lines on black background, fully opaque,
rem writes output %2
rem by extending pairs of line-ends that are within proximity threshold %3 of each other.
rem %4 is method for holes:
rem   circles  holes are circles of given radius centred on centres between line-ends
rem   squares  holes are squares of given radius centred on centres between line-ends
rem   lines    holes are lines with rounded ends with spsLINE_THK_INT between line-ends
rem
rem Also uses:
rem   skelLineEnds if 1, skeletonises the output.
@rem
@rem Updated:
@rem   6-August-2022 for IM v7
@rem

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 ele

if not "%2"=="" if not "%2"=="." set OUTFILE=%2

set PROX_THRESH=%3
if "%PROX_THRESH%"=="." set PROX_THRESH=
if "%PROX_THRESH%"=="" set PROX_THRESH=10

set METHOD=%4
if "%METHOD%"=="." set METHOD=
if "%METHOD%"=="" set METHOD=circles

call %PICTBAT%skelPS ^
  %INFILE% %BASENAME%_%sioCODE%.png
if ERRORLEVEL 1 exit /B 1

echo %0: done skelPS

call %PICTBAT%pointsRdx ^
  %spsENDS% ^
  %spsCSVFILE% %BASENAME%_lines_%sioCODE%.scr %PROX_THRESH%
if ERRORLEVEL 1 exit /B 1

echo %0: done pointsRdx

set /A RAD=%PROX_THRESH%/2

set HOLES_SCR=%BASENAME%_holes_%sioCODE%.scr

echo %0: spsLINE_THK_INT=%spsLINE_THK_INT%

set sLINWID=

if /I %METHOD%==circles (
  call %PICTBAT%lines2circs %prdxOUTFILE% %HOLES_SCR% %RAD%
  if ERRORLEVEL 1 exit /B 1
) else if /I %METHOD%==squares (
  call %PICTBAT%lines2sqs %prdxOUTFILE% %HOLES_SCR% %RAD%
  if ERRORLEVEL 1 exit /B 1
) else if /I %METHOD%==lines (
  rem FIXME: more than the line width?
  set /A wider=2*%spsLINE_THK_INT%
  call %PICTBAT%lines2RndedLines %prdxOUTFILE% %HOLES_SCR%
  if ERRORLEVEL 1 exit /B 1
  set sLINWID=-stroke Black -linewidth !wider!
) else (
  echo %0: Unknown METHOD=%METHOD%
  exit /B 1
)

echo %0: HOLES_SCR=%HOLES_SCR%

type %HOLES_SCR%

if "%skelLineEnds%"=="1" (
  set sSKEL=-morphology Thinning:-1 skeleton -clamp
) else (
  set sSKEL=
)

set /A nLSR=(%RAD%+3)/4

set /A WR=%spsLINE_THK_INT%+2

set /A WR2=3*%WR%

if %nLSR% LSS %WR2% set nLSR=%WR2%

set /A nLSRp=%nLSR%+2

set FH_PARAMS=window_radius %WR% lsr %nLSR% auto_limit_search off auto_repeat v

echo FH_PARAMS=%FH_PARAMS%

:: FIXME: wrong check
if "%l2cCIRCS%"=="0" (

  echo %0: no holes

  %IMG7%magick ^
    %INFILE% ^
    %OUTFILE%
) else (

  echo %0: with holes sSKEL="%sSKEL%"

  %IM7DEV%magick ^
    %INFILE% ^
    +write mpr:IMG ^
    +antialias ^
    ^( mpr:IMG ^
       -fill White -colorize 100 ^
       -fill Black ^
       %sLINWID% ^
       -draw "@%HOLES_SCR%" ^
       mpr:IMG -compose Lighten -composite ^
       -fill Black +opaque White ^
    ^) ^
    -antialias ^
    -alpha off -compose CopyOpacity -composite ^
    -process 'fillholespri %FH_PARAMS% dt 0.01 copy window' ^
    -process 'fillholespri %FH_PARAMS% dt 0.1' ^
    -process 'fillholespri %FH_PARAMS% dt 0.5' ^
    -process 'fillholespri %FH_PARAMS%' ^
    -compose Over ^
    -alpha off ^
    %OUTFILE%

    if not "%sSKEL%"=="" %IMG7%magick ^
      %OUTFILE% ^
      %sSKEL% ^
      -alpha off ^
      %OUTFILE%

  echo %0: with holes done

)

if ERRORLEVEL 1 exit /B 1


call echoRestore

endlocal & set %sioCODE%OUTFILE=%OUTFILE%

TjuncLineEnds.bat

rem From %1 image of white lines on black background, fully opaque,
rem writes output %2
rem by joining line-ends with other lines that are within proximity threshold %3.
rem
rem Also uses:
rem   skelLineEnds if 1, skeletonises the output.
@rem
@rem Updated:
@rem   6-August-2022 for IM v7
@rem

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 tjle

set TMP_IN=%BASENAME%_tjle_in.miff
set LINE_SCR=%BASENAME%_tjle.scr

del %LINE_SCR% 2>nul

if not "%2"=="" if not "%2"=="." set OUTFILE=%2

set PROX_THRESH=%3
if "%PROX_THRESH%"=="." set PROX_THRESH=
if "%PROX_THRESH%"=="" set PROX_THRESH=10

%IMG7%magick %INFILE% %TMP_IN%

call %PICTBAT%skelPS ^
  %TMP_IN% %BASENAME%_%sioCODE%.png
if ERRORLEVEL 1 exit /B 1

set II=0
set /A CRP_WH=2*%PROX_THRESH%+1
for /F "tokens=1,2 delims=, " %%X in (%spsCSVFILE%) do (
  rem echo %0: %%X,%%Y

  set LE_X=%%X
  set LE_Y=%%Y

  set /A CRP_L=!LE_X!-%PROX_THRESH%
  set /A CRP_T=!LE_Y!-%PROX_THRESH%

  set nwCX=%PROX_THRESH%
  set nwCY=%PROX_THRESH%
  set CRP_W=%CRP_WH%
  set CRP_H=%CRP_WH%

  if !CRP_L! LSS 0 (
    set /A nwCX+=!CRP_L!
    set /A CRP_W+=!CRP_L!
    set CRP_L=0
  )

  if !CRP_T! LSS 0 (
    set /A nwCY+=!CRP_T!
    set /A CRP_H+=!CRP_T!
    set CRP_T=0
  )

  for /F "usebackq tokens=1-3 delims=, " %%A in (`%IM7DEV%magick ^
    %TMP_IN% ^
    -fill Black +opaque White ^
    -crop !CRP_W!x!CRP_H!+!CRP_L!+!CRP_T! +repage ^
    -draw "color !nwCX!,!nwCY! floodfill" ^
    -process 'nearestwhite cx !nwCX! cy !nwCY!' ^
    NULL: 2^>^&1`) do (
    if "%%A"=="nearestwhite:" (
      set ncCST_X=%%B
      if not "%%B"=="none" (
        set ncCST_Y=%%C
        set /A ML_X=!CRP_L!+%%B
        set /A ML_Y=!CRP_T!+%%C
      )
    )
  )

  rem echo %0: ncCST_X=!ncCST_X! ncCST_Y=!ncCST_Y!

  if not "!ncCST_X!"=="none" (

    rem FIXME: check hypot

    echo line !LE_X!,!LE_Y! !ML_X!,!ML_Y!

    echo line !LE_X!,!LE_Y! !ML_X!,!ML_Y! >>!LINE_SCR!
  )

  set /A II+=1
)
if ERRORLEVEL 1 exit /B 1


if "%skelLineEnds%"=="1" (
  set sSKEL=-morphology Thinning:-1 skeleton -clamp
) else (
  set sSKEL=
)

if exist %LINE_SCR% (
  rem type %LINE_SCR%

  %IMG7%magick ^
    %INFILE% ^
    +antialias ^
    -stroke White ^
    -strokewidth %spsLINE_THK_INT% ^
    -draw "@%LINE_SCR%" ^
    %sSKEL% ^
    -alpha off ^
    %OUTFILE%
) else (
  %IMG7%magick ^
    %INFILE% ^
    %sSKEL% ^
    -alpha off ^
    %OUTFILE%
)

call echoRestore

endlocal & set %sioCODE%OUTFILE=%OUTFILE%

combLineEnds.bat

rem From image of white lines on black background,
rem writes output %2
rem combining methods, each with proximity threshold %3.

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

@setlocal enabledelayedexpansion

rem @call echoOffSave

call %PICTBAT%setInOut %1 cle

if not "%2"=="" if not "%2"=="." set OUTFILE=%2

set PROX_THRESH=%3
if "%PROX_THRESH%"=="" set PROX_THRESH=.


call %PICTBAT%extLineEnds %INFILE% %OUTFILE% %PROX_THRESH%
if ERRORLEVEL 1 exit /B 1

call %PICTBAT%proxLineEnds %OUTFILE% %OUTFILE% %PROX_THRESH%
if ERRORLEVEL 1 exit /B 1

call %PICTBAT%TjuncLineEnds %OUTFILE% %OUTFILE% %PROX_THRESH%
if ERRORLEVEL 1 exit /B 1


call echoRestore

@endlocal & set %sioCODE%OUTFILE=%OUTFILE%

lines2circs.bat

rem Given %1 is text file with lines (eg from pointsDist.bat)
rem writes circles to text file %2 [default l2c.lis]
rem of radius %3 [10]
rem centred on mid-points of coordinate-pairs from %1.

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

@setlocal enabledelayedexpansion

@call echoOffSave


set INFILE=%1

if not exist %INFILE% exit /B 1


set OUTFILE=%2
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=l2c.lis

del %OUTFILE% 2>nul

set RAD=%3
if "%RAD%"=="." set RAD=
if "%RAD%"=="" set RAD=10

set nCIRCS=0
(for /F "tokens=2-5 delims=, " %%A in (%INFILE%) do (
  set /A XC=^(%%A+%%C^)/2
  set /A YC=^(%%B+%%D^)/2
  set /A X2=!XC!+%RAD%
  echo circle !XC!,!YC! !X2!,!YC!
  set /A nCIRCS+=1
)) >%OUTFILE%


call echoRestore

endlocal & set l2cOUTFILE=%OUTFILE%&set l2cCIRCS=%nCIRCS%

lines2sqs.bat

rem Given %1 is text file with lines (eg from pointsDist.bat)
rem writes squares to text file %2 [default l2c.lis]
rem of radius %3 [10]
rem centred on mid-points of coordinate-pairs from %1.

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

@setlocal enabledelayedexpansion

@call echoOffSave


set INFILE=%1

if not exist %INFILE% exit /B 1


set OUTFILE=%2
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=l2c.lis

set RAD=%3
if "%RAD%"=="." set RAD=
if "%RAD%"=="" set RAD=10

(for /F "tokens=2-5 delims=, " %%A in (%INFILE%) do (
  set /A X0=^(%%A+%%C^)/2-%RAD%
  set /A Y0=^(%%B+%%D^)/2-%RAD%
  set /A X1=^(%%A+%%C^)/2+%RAD%
  set /A Y1=^(%%B+%%D^)/2+%RAD%
  echo rectangle !X0!,!Y0! !X1!,!Y1!
)) >%OUTFILE%


call echoRestore

endlocal

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

%IMG7%magick -version
Version: ImageMagick 7.1.0-49 Q16-HDRI x64 7a3f3f1:20220924 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenCL 
Delegates (built-in): bzlib cairo freetype gslib heic jng jp2 jpeg jxl lcms lqr lzma openexr pangocairo png ps raqm raw rsvg tiff webp xml zip zlib
Compiler: Visual Studio 2022 (193331630)

To improve internet download speeds, some images may have been automatically converted (by ImageMagick, of course) from PNG or TIFF or MIFF to JPG.

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


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.0 17-June-2016.

Page created 29-Sep-2022 06:36:54.

Copyright © 2022 Alan Gibson.