snibgo's ImageMagick pages

Alignment by matching points

When the camera has twisted around the z-axis between photographs, registering the images is difficult. No areas in one image match areas in the other. We want to find alignment when the image has scaled, rotated, translated and perspectived.

We start by finding points (instead of areas) in each image that might match each other. We use one of the methods from Details, details to identify features in one image, ie areas of high variation, high entropy. These are likely to be distinctive in the other image, reducing false matches.

Set up initial conditions:

set WEB_SIZE=-resize 500x500

Sample images

We start with two images. We want to find points on the first image that correspond to points on the other image. From that list, we will distort the second image so it registers with the first.

The camera has clearly rotated. The camera position has also moved slightly. A perspective transformation cannot align these images perfectly.

The two photographs have undergone similar, but not identical, processing.

All operations on this page are performed on full-size images, roughly 7500x5000 pixels. These are then reduced in size, and converted to JPG, for the web.

The source image.

set ap_ALI_SRC=\pictures\20140602\AGA_1858_sat.tiff

%IM%convert ^
  %ap_ALI_SRC% ^
  %WEB_SIZE% ^
  ap_ALI_SRC_sm.jpg
ap_ALI_SRC_sm.jpg

Another image.

set ap_OTH_SRC=\pictures\20140602\AGA_1860_sat.tiff

%IM%convert ^
  %ap_OTH_SRC% ^
  %WEB_SIZE% ^
  ap_OTH_SRC_sm.jpg
ap_OTH_SRC_sm.jpg

Find points

For both images, we find six points of interest, using one of the methods from Details, details. The red circles in the debug image are the exclusion zones, showing the minimum distance between features.

Why only six? Because the time required is proportional to n4.

Use a difference between two blurs to find detail.

call %PICTBAT%twoBlrDiff %ap_ALI_SRC%

We will make crops 100x100, so we don't want to find points within 50 pixels of an edge.

call %PICTBAT%blackEdge %tbdOUTFILE% 10 1
set SAVE_A_BE=%beOUTFILE%

We find the six lightest points.

set nlDEBUG=1
call %PICTBAT%getPointsize %ap_ALI_SRC% 50
set nlPOINTSIZE=%gpPointsize%
set nlSTROKEWIDTH=10

call %PICTBAT%nLightest %beOUTFILE% 6 >ap_ali_coords.lis

%IM%convert ^
  %nlDEBUG_FILE% ^
  %WEB_SIZE% ^
  ap_ali_coords_sm.jpg
ap_ali_coords_sm.jpg

For the other image, use a difference between two blurs to find detail.

call %PICTBAT%twoBlrDiff %ap_OTH_SRC%

We will make crops 100x100, so we don't want to find points with 50 pixels of an edge.

call %PICTBAT%blackEdge %tbdOUTFILE% 10 1

We find the six lightest points.

set nlDEBUG=1
call %PICTBAT%getPointsize %ap_OTH_SRC% 50
set nlPOINTSIZE=%gpPointsize%
set nlSTROKEWIDTH=10

call %PICTBAT%nLightest %beOUTFILE% 6 >ap_oth_coords.lis

%IM%convert ^
  %nlDEBUG_FILE% ^
  %WEB_SIZE% ^
  ap_oth_coords_sm.jpg
ap_oth_coords_sm.jpg

We can see by eye that the following points correspond:

ALI_SRC OTH_SRC
0 1
2 0
3 2
4 4

ALI_SRC point 1 is close to OTH_SRC point 3.

This is a benchark we will use later, to confirm the automated results.

How do we do automatically find points that correspond?

One possibility would be to make crops from one image and search for them in the other area. With arbitrary rotations and scaling, this is difficult.

Another possibility, developed here, is to consider all possible alignments that are defined by the correspondence of two points in one image with two points in the other image.

How many alignments are there? If we have n points, there are (n-1) * n/2 * n * (n-1) possible alignments. For n=6, there are 450 possible alignments. For n=10, there are 4050 possible alignments.

How do we know when an alignment is good? We could distort the image, then check for crops of one image in the other. This would be expensive.

Instead, we look for two aligments that are similar. The principle is that most possible alignments are incorrect, but some are correct. The ones that are correct will be similar to each other. By finding the two alignments that are closest to each other, we should have found two correct alignments.

It is possible that the two closest alignments are both incorrect. This obviously occurs when the points have been badly chosen and there are no correct alignments. Theoretically, it could also happen when there are correct alignments, but two incorrect alignments happen to be close to each other. I haven't seen this happen (yet).

Comparing all alignments with each other would be feasible in a compiled program, but not in a Windows BAT script. Instead, we simplfy the problem. We will consider similarity only for the TX and TY parameters. We do this in two stages. First, we add the absolute values of TX and TY. This gives us a single number that we can sort on; two similar alignments will have similar numbers.

For convenience, we read the point coordinates into an array of structures. We want to distort the second to match the first, so we use OTH_SRC as image A, and ALI_SRC as image B.

set lstA=ap_oth_coords.lis
set lstB=ap_ali_coords.lis

set alSep=,
call %PICTBAT%appendLines %lstA% %lstB% >ap_corrPnts.csv
call %PICTBAT%readCoordPairs ap_corrPnts.csv ap_corrPnts 1 2 3 4

set ap_corrPnts 
ap_corrPnts.COORDS= 6622,3910,4338,3019 3584,2988,1884,3121 1275,1010,6417,707 2534,932,1397,4307 4585,1413,3383,1368 1820,1194,3893,3439
ap_corrPnts.nPAIRS=6
ap_corrPnts[0].A.x=6622
ap_corrPnts[0].A.y=3910
ap_corrPnts[0].B.x=4338
ap_corrPnts[0].B.y=3019
ap_corrPnts[1].A.x=3584
ap_corrPnts[1].A.y=2988
ap_corrPnts[1].B.x=1884
ap_corrPnts[1].B.y=3121
ap_corrPnts[2].A.x=1275
ap_corrPnts[2].A.y=1010
ap_corrPnts[2].B.x=6417
ap_corrPnts[2].B.y=707
ap_corrPnts[3].A.x=2534
ap_corrPnts[3].A.y=932
ap_corrPnts[3].B.x=1397
ap_corrPnts[3].B.y=4307
ap_corrPnts[4].A.x=4585
ap_corrPnts[4].A.y=1413
ap_corrPnts[4].B.x=3383
ap_corrPnts[4].B.y=1368
ap_corrPnts[5].A.x=1820
ap_corrPnts[5].A.y=1194
ap_corrPnts[5].B.x=3893
ap_corrPnts[5].B.y=3439

An alignment is defined by two points in one image corresponding to two points in the other image. We loop through all possible combinations of two different points in A and two different points in B. So there are four levels of nested loops.

Each pass calls getPerspParams to call IM "convert -distort" for the transformation parameters. As we are using just two points from the two images, this is overkill. The transformation can only be scale, rotate and translate, with no shear or perspective, and is easy to calculate from the coordinates. (Two points in two images define two lines. The ratio of the lengths gives the scale; the angle between the lines gives the rotation. Apply the scale and rotatation to one point, subtract its (x,y) from the (x,y) of the point in the other image, and that is the translation.)

set /A nMin1=%ap_corrPnts.nPAIRS%-1
set /A nMin2=%ap_corrPnts.nPAIRS%-2

rem i,j are indexes into image A
rem k,l are indexes into image B

set XYLIST=ap_xylist.csv
del %XYLIST% 2>nul

call echoOffSave
rem Visit every possible alignment.
rem For each one, calculate the sum of the absolute TX and TY parameters.
rem
for /L %%i in (0,1,%nMin2%) do (
  set /A j0=%%i+1
  for /L %%j in (!j0!,1,%nMin1%) do (
    echo %%i %%j

    for /L %%k in (0,1,%nMin1%) do (
      set /A l0=%%k+1
      for /L %%l in (0,1,%nMin1%) do (

        if not %%k==%%l (
          echo ijkl = %%i %%j %%k %%l
          set TwoPairs=!ap_corrPnts[%%i].A.x!,!ap_corrPnts[%%i].A.y!,!ap_corrPnts[%%k].B.x!,!ap_corrPnts[%%k].B.y! !ap_corrPnts[%%j].A.x!,!ap_corrPnts[%%j].A.y!,!ap_corrPnts[%%l].B.x!,!ap_corrPnts[%%l].B.y!
          echo !TwoPairs!

          call %PICTBAT%getPerspParams TwoPairs pp

          for /F "usebackq" %%L in (`%IM%identify ^
            -format "sumAbs=%%[fx:abs(!pp.TX!)+abs(!pp.TY!)]" ^
            xc:`) do set %%L

          echo !sumAbs!,!pp.TX!,!pp.TY!, %%i,%%j, %%k,%%l >>%XYLIST%
        )
      )
    )
  )
)
call echoRestore

The created file, %XYLIST%, has 450 lines, one for each possible alignment. (Perhaps we might restrict the output to alignments where |TX| < 2 * image_width && |TY| < 2 * image_height.)

Then we sort on the sum of the absolute TX and TY parameters, and append each line with the next line.

Aside: cSort.exe is used here to sort in order of the first, then second, columns. The data is interpreted as floating-point numbers. cSort.exe is my own unpublished software. If the only values were 0.000 to 9.999, a text sorter such as Windows sort comand would work. Of course, we only want the lowest value, so sorting is overkill.

cSort /i%XYLIST% /o%XYLIST% /k0,1

cHead /i%XYLIST% /oap_xtm1.lis /h1 /x
call %PICTBAT%appendLines %XYLIST% ap_xtm1.lis >ap_comp.csv

The file ap_comp.csv contains one line per pair of algnments that might be similar.

We refine the comparison of each alignment with the next. Two similar alignments will have similar sums of absolute values of TX and TY, and similar TX, and similar TY. So by taking differences and adding them together, we then have a measure of similarity of pairs of alignments. This is quite slow because we are calling "identify" to do the calculation.

An input line may say that a point from A corresponds to two points on B, or vice versa. If this happens, we disregard the line.

rem Add the absolute values of the differences of:
rem   the sum of the absolute TX and TY parameters
rem   the TX parameters
rem   the TY parameters
rem
rem We assume that the smallest value gives the best pair of alignments.
rem
rem IsOk was:
rem   IsOk=%%[fx:(%%D==%%K)==(%%E==%%L)&&(%%F==%%M)==(%%G==%%N)?1:0]
rem 
set COMP_SRT=ap_comp_srt.csv
del %COMP_SRT% 2>nul
for /F "tokens=1-14 delims=, " %%A in (ap_comp.csv) do (

  for /F "usebackq" %%L in (`%IM%identify ^
    -format "delta=%%[fx:abs(%%H-(%%A))+abs(%%I-(%%B))+abs(%%J-(%%C))]\nIsOk=%%[fx:(%%D==%%K)==(%%F==%%M)&&(%%D==%%L)==(%%F==%%N)&&(%%E==%%K)==(%%G==%%M)&&(%%E==%%L)==(%%G==%%N)]" ^
    xc:`) do set %%L

  if !IsOk!==1 echo !delta!, %%A,%%B,%%C,%%D,%%E,%%F,%%G, %%H,%%I,%%J,%%K,%%L,%%M,%%N >>%COMP_SRT%
)

We sort, and take the lowest measure:

cSort /i%COMP_SRT% /o%COMP_SRT% /k0
cHead /i%COMP_SRT% /h1 /ocomp_srt1.csv

Aside: Just for interest, here are the first 12 lines:

cHead /i%COMP_SRT% /h12 /ocomp_srt12.csv
3.49783, 5113.52,-90.937847,5022.586328,0,3,2,1, 5115.27,-91.030727,5024.241277,0,2,2,3 
3.95831, 5115.27,-91.030727,5024.241277,0,2,2,3, 5117.25,-92.117819,5025.132494,2,3,3,1 
62.3611, 5202.18,-177.403304,5024.779315,2,4,3,4, 5204.74,-148.782765,5055.959857,1,2,0,3 
203.661, 3375.74,836.743469,2538.992260,2,4,1,0, 3420.93,938.576339,2482.353769,1,5,2,4 
868.392, 4267.81,1232.887471,3034.921140,0,2,0,1, 4283.19,814.071308,3469.116696,2,4,1,4 
906.195, 8730.75,4648.181839,4082.565474,2,4,5,1, 8743.84,4208.178077,4535.666420,1,2,4,5 
979.459, 4975.69,391.785886,4583.904943,0,5,5,3, 4994.34,-489.727440,4504.608868,1,2,2,1 
1003.03, 4170.44,137.343550,4033.097145,2,4,3,5, 4170.66,638.861079,3531.803255,0,3,0,1 
1025.67, 4074.06,3697.746854,376.317830,2,4,4,5, 4096.58,3207.428092,889.148968,0,2,0,4 
1083.84, 7101.48,3600.925307,3500.557734,2,4,5,0, 7144.88,3102.400881,4042.478108,0,2,2,5 
1097.49, 11815.7,6780.073644,5035.657462,2,3,0,1, 11818.6,6234.215141,5584.385760,0,4,4,5 
1110.73, 4283.19,814.071308,3469.116696,2,4,1,4, 4314.21,1369.437489,2944.768708,0,2,5,1 

For simpler matches, from simpler camera movements, most of these would be correct. For this example, the second line is correct but all the rest are wrong. The first line has incorrectly corresponded ALI_SRC point 0 with OTH_SRC point 3. The other two correspondences in this line (2 with 0, and 3 with 2) are correct.

The file comp_srt1.csv is:

3.49783, 5113.52,-90.937847,5022.586328,0,3,2,1, 5115.27,-91.030727,5024.241277,0,2,2,3 

We now have 3 or 4 correspondences between the images. (The algorithm can give 4, but seems to always give only 3 when the correspondences are correct.) We could add each possible new correspondence to the list, and iterate. But we must beware: not all points will have a corresponding point in the other image.

The following messy code find the 3 or 4 correspondences.

rem Get the numbers of the matching points.
rem There will be 3 or 4 matches.
rem Either of the last two may be equal to the first two.

for /F "tokens=5-8,12-15 delims=, " %%A in (comp_srt1.csv) do (
  echo %%A,%%B,%%C,%%D, %%E,%%F,%%G,%%H

  set ap_corrNdx[0].A=%%A
  set ap_corrNdx[1].A=%%B
  set ap_corrNdx[0].B=%%C
  set ap_corrNdx[1].B=%%D

  set ap_corrNdx.nPAIRS=2

  set /A diff2=^(%%E-%%A^)*^(%%G-%%C^)*^(%%E-%%B^)*^(%%G-%%D^)
  set /A diff3=^(%%F-%%A^)*^(%%H-%%C^)*^(%%F-%%B^)*^(%%H-%%D^)

  if not !diff2!==0 (
    set ap_corrNdx[2].A=%%E
    set ap_corrNdx[2].B=%%G
    set ap_corrNdx.nPAIRS=3
  )

  if not !diff3!==0 (
    set ap_corrNdx[!ap_corrNdx.nPAIRS!].A=%%F
    set ap_corrNdx[!ap_corrNdx.nPAIRS!].B=%%H
    set /A ap_corrNdx.nPAIRS+=1
  )

  echo diff2=!diff2! diff3=!diff3!
)

set ap_corrNdx 
ap_corrNdx.nPAIRS=3
ap_corrNdx[0].A=0
ap_corrNdx[0].B=2
ap_corrNdx[1].A=3
ap_corrNdx[1].B=1
ap_corrNdx[2].A=2
ap_corrNdx[2].B=3

Above, we found the best transformation from two points that corresponded. Now we know three points that correspond. We can get the coordinates, and feed them to a "-distort".

rem Get the coords of the 3 or 4 points.
set /A nLAST=ap_corrNdx.nPAIRS-1

set sCOORD=
for /L %%i in (0,1,%nLAST%) do (
  set /A ndxA=ap_corrNdx[%%i].A
  set /A ndxB=ap_corrNdx[%%i].B
  set /A ax=ap_corrPnts[!ndxA!].A.x
  set /A ay=ap_corrPnts[!ndxA!].A.y
  set /A bx=ap_corrPnts[!ndxB!].B.x
  set /A by=ap_corrPnts[!ndxB!].B.y
  set sCOORD=!sCOORD! !ax!,!ay!,!bx!,!by!
)
echo sCOORD=%sCOORD% 
sCOORD= 6622,3910,6417,707 2534,932,1884,3121 1275,1010,1397,4307 
%IM%convert ^
  xc: ^
  -verbose ^
  -distort Perspective "%sCOORD%" ^
  NULL: 
Affine Projection:
  -distort AffineProjection \
      '0.443409,-0.914466,0.913481,0.444707,-90.961506,5023.790215'
Affine Distort, FX Equivelent:
  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;
       xx=+0.430695*ii -0.884698*jj +4483.713031;
       yy=+0.885652*ii +0.429437*jj -2076.842681;
       p{ xx-page.x-.5, yy-page.y-.5 }' \
xc:=> XC 1x1 1x1+0+0 16-bit sRGB 0.000u 0:00.000

Visual check

Note that we need the "+" form of "distort". We also save the transformed version of OTH_SRC, for use later. The file type needs to be capable of recording negative offsets. PNG would be fine, but not TIFF.

set OTH_XFORM=%TEMP%\ap_oth_xf.miff

%IM%convert ^
  %ap_ALI_SRC% ^
  ( %ap_OTH_SRC% ^
    -virtual-pixel Black ^
    +distort Perspective "%sCOORD%" ^
    -write %OTH_XFORM% ^
    -alpha set -channel A -evaluate Multiply 0.5 ^
  ) ^
  -layers merge +repage ^
  %WEB_SIZE% ^
  ap_vis_chk.jpg
ap_vis_chk.jpg

Looking closely at the full-size image, the rear wheel of the red car, and the wing mirror of the yellow car, are both correct. The overall scale, rotate and translate is good, and this is all we can expect from a 3-point match.

Using a script

We put most of the above into a script, alignPnt.bat, that takes two CSV files of coordinates, and returns 3 or 4 pairs of corresponding coordinates.

call %PICTBAT%alignPnt ap_oth_coords.lis ap_ali_coords.lis
echo %apCOORDS% 
 6622,3910,6417,707 2534,932,1884,3121 1275,1010,1397,4307 

Match areas

Now we know an aproximate alignment, we can find a more exact alignment by:

  1. iterating the above method to find more correspondences;
  2. or distorting the image according to the found correspondences, then using the method given in Simple alignment by matching areas.

As the method so far has "latched on" to an incorrect solution, the first is unwise, and we will use the second.

We can take areas around the original six points from ALI_SRC and search for these in the transformed ALI_OTH.

For details of alignArea.bat, see Simple alignment by matching areas.

call %PICTBAT%alignArea %ap_ALI_SRC% %OTH_XFORM% ap_ali_coords.lis
if ERRORLEVEL 1 exit /B 1

type %aaCOORDS_CSV% >aa_coords.csv
4355,4721,4338,3019,-17,-1702,1702.0849 , 0 
2069,4794,1884,3121,-185,-1673,1683.19755 , 1 
1489,6031,1397,4307,-92,-1724,1726.45301 , 3 
3325,3183,3383,1368,58,-1815,1815.92649 , 4 
3851,5229,3893,3439,42,-1790,1790.49267 , 5 

We have found five of the six areas. We can try an alignment with these:

%IM%convert ^
  %ap_ALI_SRC% ^
  ( %OTH_XFORM% ^
    -virtual-pixel Black ^
    +distort Shepards "%aaCOORDS%" ^
    -alpha set -channel A -evaluate Multiply 0.5 ^
  ) ^
  -layers merge ^
  -write x.png ^
  +repage ^
  %WEB_SIZE% ^
  ap_vis_chk2.jpg
ap_vis_chk2.jpg

The five points are perfect, but intermediate areas are not good. A larger number of corresponding points should improve the result.

We find as many lightest points as we can.

set nlDEBUG=1
call %PICTBAT%getPointsize %ap_ALI_SRC% 50
set nlPOINTSIZE=%gpPointsize%
set nlSTROKEWIDTH=10

call %PICTBAT%nLightest %SAVE_A_BE% 9999 >ap_ali_coords2.lis

%IM%convert ^
  %nlDEBUG_FILE% ^
  %WEB_SIZE% ^
  ap_ali_coords2_sm.jpg
ap_ali_coords2_sm.jpg

Make the crops and search for them in the distorted OTH_SRC:

call %PICTBAT%alignArea %ap_ALI_SRC% %OTH_XFORM% ap_ali_coords2.lis
if ERRORLEVEL 1 exit /B 1

type %aaCOORDS_CSV% >aa_coords2.csv
4355,4721,4338,3019,-17,-1702,1702.0849 , 0 
2069,4794,1884,3121,-185,-1673,1683.19755 , 1 
1489,6031,1397,4307,-92,-1724,1726.45301 , 3 
3325,3183,3383,1368,58,-1815,1815.92649 , 4 
3851,5229,3893,3439,42,-1790,1790.49267 , 5 
4862,4552,4909,2758,47,-1794,1794.61556 , 6 
1899,5614,1794,3895,-105,-1719,1722.20382 , 7 
840,6128,744,4403,-96,-1725,1727.66924 , 8 
4628,2773,4575,961,-53,-1812,1812.77494 , 9 
2650,4531,2496,2841,-154,-1690,1697.00206 , 10 
1383,5275,1196,3601,-187,-1674,1684.41236 , 11 
3479,4671,3519,2876,40,-1795,1795.44563 , 13 
3339,2566,3096,959,-243,-1607,1625.26859 , 14 
4187,3443,4160,1682,-27,-1761,1761.20697 , 17 
2573,4000,2616,2193,43,-1807,1807.51155 , 18 
955,5531,767,3856,-188,-1675,1685.51743 , 19 
4496,4027,4541,2227,45,-1800,1800.56241 , 21 
1539,4360,1356,2691,-183,-1669,1679.00268 , 22 
2812,2863,2601,1227,-211,-1636,1649.55054 , 23 
2396,3187,2162,1567,-234,-1620,1636.81276 , 26 
3573,5724,3631,3934,58,-1790,1790.93942 , 27 
2187,4223,1955,2585,-232,-1638,1654.34821 , 29 
3755,2805,3814,990,59,-1815,1815.9587 , 34 
2808,3495,2866,1671,58,-1824,1824.92192 , 35 
1814,3850,1583,2220,-231,-1630,1646.28703 , 36 
5880,2351,5614,734,-266,-1617,1638.73274 , 39 
3823,2117,3575,518,-248,-1599,1618.11773 , 41 
4482,2195,4248,577,-234,-1618,1634.83332 , 44 
3372,3662,3339,1903,-33,-1759,1759.30952 , 45 
5388,2272,5123,661,-265,-1611,1632.64999 , 48 
2300,3727,2070,2095,-230,-1632,1648.12742 , 61 

With nLightest.bat defaults, a square image has 50 points. This image has aspect ratio 1:1.5, so around 75 points. (Actually 79.) Searching this size of image takes around 8 seconds per point. So the search takes around ten minutes.

From the list, we see that almost all beyond the 50th were outliers, and choosing to find just the 24 lightest points would have found most non-outliers.

%IM%convert ^
  %ap_ALI_SRC% ^
  ( %OTH_XFORM% ^
    -virtual-pixel Black ^
    +distort Shepards "%aaCOORDS%" ^
    -alpha set -channel A -evaluate Multiply 0.5 ^
  ) ^
  -layers merge ^
  -write x.png ^
  +repage ^
  %WEB_SIZE% ^
  ap_vis_chk3.jpg
ap_vis_chk3.jpg

The yellow car is rather fuzzy. The best point for it, number 2, was removed as an outlier. The red car and lorry are okay. Bollards and fencing (hardly visible in the web version) are now aligned, as are most background trees. The most obvious problem is in the framework and roof of the trolley shelter, which is weirdly warped.

This suggests a possible refinement: the radius of the exclusion circle might vary, beginning small, and increasing. This would give a greater weight to areas of high detail. This is desirable: parts of the trees are as warped as the trolley shelter, but this isn't as visible.

Scripts

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

getPerspParams.bat

rem Given %1 is the name of an environment variable containing coordinate pairs,
rem returns perspective parameters in structure %2.SY etc.

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

@setlocal enabledelayedexpansion

@call echoOffSave

rem NOTE:
rem This can give stupid results, eg for input:
rem   "0,0,5,5 100,100,105,105 70,70,65,72"
rem 
rem IM gives:
rem 
rem Affine Projection:
rem   -distort AffineProjection \
rem       '1428571428571413.200000,428571428571413.440000,-1428571428571413.200000,
rem  -428571428571413.440000,0.000000,0.000000'

set COORD_PAIRS=!%1!

set /A nLINE=0

for /F "usebackq tokens=*" %%L in (`%IM%convert ^
  xc: ^
  -precision 9 ^
  -verbose ^
  -distort Perspective "%COORD_PAIRS%" ^
  +verbose ^
  NULL: 2^>^&1`) do (
  set LINE=%%L
  set LINE=!LINE:^>=GREATER_THAN!
  set LINE=!LINE:^<=LESS_THAN!
  set LINES[!nLINE!]=!LINE!
  set /A nLINE+=1
  rem echo LINE=!LINE!
)

rem echo nLINE=%nLINE%

rem echo 0: %LINES[0]%
rem echo 1: %LINES[1]%
rem echo 2: %LINES[2]%
rem echo 3: %LINES[3]%

rem echo 4: {%LINES[4]%}
rem echo 5: {%LINES[5]%}
rem echo 6: {%LINES[6]%}
rem echo 7: {%LINES[7]%}
rem echo 8: {%LINES[8]%}
rem echo 9: {%LINES[9]%}

rem We could also save the "-distort ..." command, if we wanted.

set IsPersp=0

for /F "tokens=1-4 delims=',: " %%A in ("%LINES[0]%") do (
  if /I "%%A" equ "Perspective" set IsPersp=1
)

rem echo IsPersp=%IsPersp%


if %IsPersp%==1 goto IsPersp

  rem Affine

  for /F "tokens=1-6 delims=', " %%A in ("%LINES[2]%") do (
    rem echo %%A %%B %%C %%D %%E %%F
    set SX=%%A
    set RX=%%B
    set RY=%%C
    set SY=%%D
    set TX=%%E
    set TY=%%F
    set PX=0
    set PY=0
  )
  set DIST_FX=%LINES[4]% %LINES[5]% %LINES[6]% %LINES[7]%

  set DIST_FX=%DIST_FX:'="%
  set DIST_FX=%DIST_FX:\=%

  goto endPersp

:IsPersp
  rem Perspective

  for /F "tokens=1-4 delims=', " %%A in ("%LINES[2]%") do (
    rem echo %%A %%B %%C %%D
    set SX=%%A
    set RY=%%B
    set TX=%%C
    set RX=%%D
  )

  for /F "tokens=1-4 delims=', " %%A in ("%LINES[3]%") do (
    rem echo %%A %%B %%C %%D
    set SY=%%A
    set TY=%%B
    set PX=%%C
    set PY=%%D
  )

  set DIST_FX=%LINES[5]% %LINES[6]% %LINES[7]% %LINES[8]% %LINES[9]%

  set DIST_FX=%DIST_FX:'="%
  set DIST_FX=%DIST_FX:\=%
  set DIST_FX=%DIST_FX:GREATER_THAN=^>%
  set DIST_FX=%DIST_FX:LESS_THAN=^<%

:endPersp

rem echo DIST_FX=%DIST_FX%

rem if not %IsPersp%==1 exit /B 1


@call echoRestore

@endlocal & set %2.SX=%SX%& set %2.RY=%RY%& set %2.TX=%TX%& set %2.RX=%RX%& set %2.SY=%SY%& set %2.TY=%TY%& set %2.PX=%PX%& set %2.PY=%PY%& set %2.DIST_FX=%DIST_FX%

blackEdge.bat

rem For image %1, makes black all pixels up to %2 from any edge.
rem %3: 0 means param 2 is pixels, 1 means param 2 is percentage. Default 0.

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

@setlocal enabledelayedexpansion

rem @call echoOffSave

call %PICTBAT%setInOut %1 be


set RTW=%2
set RTH=%2

for /F "usebackq" %%L in (`%IM%convert ^
  -ping ^
  %INFILE% ^
  -format "WW=%%w\nHH=%%h\nWm1=%%[fx:w-1]\nHm1=%%[fx:h-1]" ^
  info:`) do set %%L

if "%3"=="1" (
  for /F "usebackq" %%L in (`%IM%identify ^
    -format "RTW=%%[fx:%WW%*%RTW%/100]\nRTH=%%[fx:%HH%*%RTH%/100]" ^
    xc:`) do set %%L
)

for /F "usebackq" %%L in (`%IM%convert ^
  -ping ^
  %INFILE% ^
  -format "WmRT=%%[fx:%WW%-%RTW%]\nHmRT=%%[fx:%HH%-%RTH%]" ^
  info:`) do set %%L


%IM%convert ^
  %INFILE% ^
  -fill #000 ^
  -draw "rectangle 0,0 %Wm1%,%RTH% rectangle 0,0 %RTW%,%Hm1% rectangle %WmRT%,0 %Wm1%,%Hm1% rectangle 0,%HmRT% %Wm1%,%Hm1% " ^
  %OUTFILE%

@call echoRestore

@endlocal & set beOUTFILE=%OUTFILE%

alignPnt.bat

rem %1 is CSV file with list of coords from image A.
rem %2 is CSV file with list of coords from image B.
rem Some points in A correspond to some points in B.
rem Returns affine or perspective transformation from B to A.
@rem
@rem Updated
@rem   3-May-2017 Weirdly, needed set lstA and B adding.
@rem


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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 ap

set lstA=%1
set lstB=%2

if not exist %lstA% exit /B 1
if not exist %lstB% exit /B 1

set TEMPLIS=%TEMP%\ap_%~n1.csv

set alSep=,
call %PICTBAT%appendLines %lstA% %lstB% >ap_corrPnts.csv
call %PICTBAT%readCoordPairs ap_corrPnts.csv ap_corrPnts 1 2 3 4

set /A nMin1=%ap_corrPnts.nPAIRS%-1
set /A nMin2=%ap_corrPnts.nPAIRS%-2

rem i,j are indexes into image A
rem k,l are indexes into image B

set XYLIST=%TEMP%\ap_xylist.csv
del %XYLIST% 2>nul

rem Visit every possible alignment.
rem For each one, calculate the sum of the absolute TX and TY parameters.
rem
for /L %%i in (0,1,%nMin2%) do (
  set /A j0=%%i+1
  for /L %%j in (!j0!,1,%nMin1%) do (
    echo %0: i=%%i j=%%j

    for /L %%k in (0,1,%nMin1%) do (
      set /A l0=%%k+1
      for /L %%l in (0,1,%nMin1%) do (

        if not %%k==%%l (
          rem echo ijkl = %%i %%j %%k %%l
          set TwoPairs=!ap_corrPnts[%%i].A.x!,!ap_corrPnts[%%i].A.y!,!ap_corrPnts[%%k].B.x!,!ap_corrPnts[%%k].B.y! !ap_corrPnts[%%j].A.x!,!ap_corrPnts[%%j].A.y!,!ap_corrPnts[%%l].B.x!,!ap_corrPnts[%%l].B.y!
          rem echo !TwoPairs!

          call %PICTBAT%getPerspParams TwoPairs pp

          for /F "usebackq" %%L in (`%IM%identify ^
            -format "sumAbs=%%[fx:abs(!pp.TX!)+abs(!pp.TY!)]" ^
            xc:`) do set %%L

          echo !sumAbs!,!pp.TX!,!pp.TY!, %%i,%%j, %%k,%%l >>%XYLIST%
        )
      )
    )
  )
)

cSort /p0 /i%XYLIST% /o%XYLIST% /k0,1

set APCOMP=%TEMP%\ap_comp.csv
cHead /i%XYLIST% /o%TEMP%\ap_xtm1.lis /h1 /x
call %PICTBAT%appendLines %XYLIST% %TEMP%\ap_xtm1.lis >%APCOMP%


rem Add the absolute values of the differences of:
rem   the sum of the absolute TX and TY parameters
rem   the TX parameters
rem   the TY parameters
rem
rem We assume that the smallest value gives the best pair of alignments.
rem
rem IsOk was:
rem   IsOk=%%[fx:(%%D==%%K)==(%%E==%%L)&&(%%F==%%M)==(%%G==%%N)?1:0]
rem 

set COMP_SRT=%TEMP%\ap_comp_srt.csv
del %COMP_SRT% 2>nul
for /F "tokens=1-14 delims=, " %%A in (%APCOMP%) do (

  for /F "usebackq" %%L in (`%IM%identify ^
    -format "delta=%%[fx:abs(%%H-(%%A))+abs(%%I-(%%B))+abs(%%J-(%%C))]\nIsOk=%%[fx:(%%D==%%K)==(%%F==%%M)&&(%%D==%%L)==(%%F==%%N)&&(%%E==%%K)==(%%G==%%M)&&(%%E==%%L)==(%%G==%%N)]" ^
    xc:`) do set %%L

  if !IsOk!==1 echo !delta!, %%A,%%B,%%C,%%D,%%E,%%F,%%G, %%H,%%I,%%J,%%K,%%L,%%M,%%N >>%COMP_SRT%
)

if not exist %COMP_SRT% (
  echo %0: No entries in COMP_SRT, %COMP_SRT%
  exit /B 1
)

cSort /p0 /i%COMP_SRT% /o%COMP_SRT% /k0
set COMP_SRT1=%TEMP%\ap_comp_srt1.csv
cHead /i%COMP_SRT% /h1 /o%COMP_SRT1%


rem Get the numbers of the matching points.
rem There will be 3 or 4 matches.
rem Either of the last two may be equal to the first two.

for /F "tokens=5-8,12-15 delims=, " %%A in (%COMP_SRT1%) do (
  echo %%A,%%B,%%C,%%D, %%E,%%F,%%G,%%H

  set ap_corrNdx[0].A=%%A
  set ap_corrNdx[1].A=%%B
  set ap_corrNdx[0].B=%%C
  set ap_corrNdx[1].B=%%D

  set ap_corrNdx.nPAIRS=2

  set /A diff2=^(%%E-%%A^)*^(%%G-%%C^)*^(%%E-%%B^)*^(%%G-%%D^)
  set /A diff3=^(%%F-%%A^)*^(%%H-%%C^)*^(%%F-%%B^)*^(%%H-%%D^)

  if not !diff2!==0 (
    set ap_corrNdx[2].A=%%E
    set ap_corrNdx[2].B=%%G
    set ap_corrNdx.nPAIRS=3
  )

  if not !diff3!==0 (
    set ap_corrNdx[!ap_corrNdx.nPAIRS!].A=%%F
    set ap_corrNdx[!ap_corrNdx.nPAIRS!].B=%%H
    set /A ap_corrNdx.nPAIRS+=1
  )

  echo diff2=!diff2! diff3=!diff3!
)

set ap_corrNdx


rem Get the coords of the 3 or 4 points.
set /A nLAST=ap_corrNdx.nPAIRS-1

set sCOORD=
for /L %%i in (0,1,%nLAST%) do (
  set /A ndxA=ap_corrNdx[%%i].A
  set /A ndxB=ap_corrNdx[%%i].B
  set /A ax=ap_corrPnts[!ndxA!].A.x
  set /A ay=ap_corrPnts[!ndxA!].A.y
  set /A bx=ap_corrPnts[!ndxB!].B.x
  set /A by=ap_corrPnts[!ndxB!].B.y
  set sCOORD=!sCOORD! !ax!,!ay!,!bx!,!by!
)
echo %0: sCOORD=%sCOORD%




@call echoRestore

@endlocal & set apCOORDS=%sCOORD%

srchImg.bat

rem  Searches image %1 for smaller image %2.
@rem  If dimensions are large, resizes to find approx location,
@rem  and searches again within cropped limits.
@rem  Assumes width and height of %2 is smaller than of %1.
@rem
@rem  Writes results to optional batch file %3. Don't use this; it is for legacy purposes only.
@rem
@rem  Also uses:
@rem    siMETRIC        defaults to RMSE
@rem    siDEBUG         if 1, creates a marked-up copy of image.
@rem    siDEBUG_STROKE  eg "-fill None -stroke #f00 -strokewidth 10" to get thick strokes
@rem    siDELTEMPSRC    if not 0, will remove temporary source (%1) files.
@rem    siDELTEMPSUB    if not 0, will remove temporary subimage (%2) files.
@rem    siDIV_DIM       Divide min dim by this, for purpose of choosing resizes. [1]
@rem    siDISSIM_THR    Dissimilarity threshold. [1]
@rem
@rem  Returns:
@rem    siCOMP_XW, siCOMP_YW coord of top-left of found area
@rem    siCOMP_FLT  RMSE difference beween %2 and found area
@rem    siCOMP_CROP=%subW%x%subH%+%COMP_XW%+%COMP_YW%  crop spec of found area
@rem    siIS_DISSIM 1 if images too dissimilar; otherwise 0

@rem Written: 31 Jan 2014
@rem Revised: 6 May 2014
@rem Revised: 31 May 2014 Don't resize src if already exists.
@rem Revised: 8 August 2016 Tidied, slightly.
@rem Revised: 9 September 2016 Added siDISSIM_THR

@rem FIXME: +repage, then add offsets (-format "%X %Y") at end.


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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 si


set siERROR=0

if "%siDEBUG%"=="1" (
  set DEBUG_OUT=%BASENAME%_si_dbg%EXT%
  if "%siDEBUG_STROKE%"=="" set siDEBUG_STROKE=-fill None -stroke #f00
)

set SRCNAME=%~n1
set SRC=%INFILE%
set SUB=%2
set fBAT=%3
if "%fBAT%"=="" set fBAT=NUL

if "%siMETRIC%"=="" set siMETRIC=RMSE

set TMPEXT=.miff
set TMPDIR=%TEMP%

set SRC_PREF=%TMPDIR%\siSrc_%SRCNAME%
set TMPSRC=%SRC_PREF%%TMPEXT%
set TMPSUB=%TMPDIR%\siSub%TMPEXT%

if not "%siDELTEMPSRC%"=="0" (
  call :delTemp %TMPSRC%
)

if not "%siDELTEMPSUB%"=="0" (
  call :delTemp %TMPSUB%
)

if not "%fBat%"=="NUL" del %fBat% 2>nul

if "%siDIV_DIM%"=="" set siDIV_DIM=1

if "%siDISSIM_THR%"=="" set siDISSIM_THR=1

set subW=
for /F "usebackq" %%L in (`%IM%convert ^
  -ping %SUB% ^
  -format "subW=%%w\nsubH=%%h\nMinDim=%%[fx:int((w<h?w:h)/%siDIV_DIM%+0.5)]" ^
  info:`) do set /A %%L

if "%subW%"=="" set siERROR=1

set OFFSET_X=
for /F "usebackq" %%L in (`%IM%identify ^
  -format "OFFSET_X=%%X\nOFFSET_Y=%%Y" ^
  %SRC%`) do set /A %%L

if "%OFFSET_X%"=="" set siERROR=1

if %siERROR%==1 exit /B 1

echo %0: MinDim=%MinDim% Offset=(%OFFSET_X%,%OFFSET_Y%)

set SIM_DISSIM=-similarity-threshold 0 -dissimilarity-threshold %siDISSIM_THR%
set IsDissim=0

if %MinDim% GEQ 10000 (
  call :ResizeSrch 0.01 1000
  call :CropResizeSrch !COMP_XW! !COMP_YW! 2000 0.2 500
  call :CropResizeSrch !COMP_XW! !COMP_YW! 1000 0.5 200
  call :CropResizeSrch !COMP_XW! !COMP_YW! 400 1 100
  call :CropResizeSrch !COMP_XW! !COMP_YW! 200 2 50
  call :CropResizeSrch !COMP_XW! !COMP_YW! 100 5 20
  call :CropResizeSrch !COMP_XW! !COMP_YW! 40 10 10
  call :CropResizeSrch !COMP_XW! !COMP_YW! 20 20 5
  call :CropResizeSrch !COMP_XW! !COMP_YW! 10 50 2
  call :CropSrch !COMP_XW! !COMP_YW! 4
) else if %MinDim% GEQ 5000 (
  call :ResizeSrch 0.2 500
  call :CropResizeSrch !COMP_XW! !COMP_YW! 1000 0.5 200
  call :CropResizeSrch !COMP_XW! !COMP_YW! 400 1 100
  call :CropResizeSrch !COMP_XW! !COMP_YW! 200 2 50
  call :CropResizeSrch !COMP_XW! !COMP_YW! 100 5 20
  call :CropResizeSrch !COMP_XW! !COMP_YW! 40 10 10
  call :CropResizeSrch !COMP_XW! !COMP_YW! 20 20 5
  call :CropResizeSrch !COMP_XW! !COMP_YW! 10 50 2
  call :CropSrch !COMP_XW! !COMP_YW! 4
) else if %MinDim% GEQ 2000 (
  call :ResizeSrch 0.5 200
  call :CropResizeSrch !COMP_XW! !COMP_YW! 400 1 100
  call :CropResizeSrch !COMP_XW! !COMP_YW! 200 2 50
  call :CropResizeSrch !COMP_XW! !COMP_YW! 100 5 20
  call :CropResizeSrch !COMP_XW! !COMP_YW! 40 10 10
  call :CropResizeSrch !COMP_XW! !COMP_YW! 20 20 5
  call :CropResizeSrch !COMP_XW! !COMP_YW! 10 50 2
  call :CropSrch !COMP_XW! !COMP_YW! 4
) else if %MinDim% GEQ 1000 (
  call :ResizeSrch 1 100
  call :CropResizeSrch !COMP_XW! !COMP_YW! 200 2 50
  call :CropResizeSrch !COMP_XW! !COMP_YW! 100 5 20
  call :CropResizeSrch !COMP_XW! !COMP_YW! 40 10 10
  call :CropResizeSrch !COMP_XW! !COMP_YW! 20 20 5
  call :CropResizeSrch !COMP_XW! !COMP_YW! 10 50 2
  call :CropSrch !COMP_XW! !COMP_YW! 4
) else if %MinDim% GEQ 500 (
  call :ResizeSrch 2 50
  call :CropResizeSrch !COMP_XW! !COMP_YW! 100 5 20
  call :CropResizeSrch !COMP_XW! !COMP_YW! 40 10 10
  call :CropResizeSrch !COMP_XW! !COMP_YW! 20 20 5
  call :CropResizeSrch !COMP_XW! !COMP_YW! 10 50 2
  call :CropSrch !COMP_XW! !COMP_YW! 4
) else if %MinDim% GEQ 200 (
  call :ResizeSrch 5 20
  call :CropResizeSrch !COMP_XW! !COMP_YW! 40 10 10
  call :CropResizeSrch !COMP_XW! !COMP_YW! 20 20 5
  call :CropResizeSrch !COMP_XW! !COMP_YW! 10 50 2
  call :CropSrch !COMP_XW! !COMP_YW! 4
) else if %MinDim% GEQ 100 (
  call :ResizeSrch 10 10
  call :CropResizeSrch !COMP_XW! !COMP_YW! 20 20 5
  call :CropResizeSrch !COMP_XW! !COMP_YW! 10 50 2
  call :CropSrch !COMP_XW! !COMP_YW! 2
) else if %MinDim% GEQ 50 (
  call :ResizeSrch 20 5
  call :CropResizeSrch !COMP_XW! !COMP_YW! 10 50 2
  call :CropSrch !COMP_XW! !COMP_YW! 2
) else if %MinDim% GEQ 20 (
  call :ResizeSrch 50 2
  call :CropSrch !COMP_XW! !COMP_YW! 2
) else if %MinDim% GEQ 10 (
  call :ResizeSrch 50 2
  call :CropSrch !COMP_XW! !COMP_YW! 2
) else (
  FOR /F "usebackq tokens=1-4 delims=()@, " %%R ^
IN (`%IM%compare ^
    %SIM_DISSIM% -subimage-search ^
    -metric %siMETRIC% %SRC% %SUB% NULL: 2^>^&1`) ^
DO (
    if /I %%U GTR A set IsDissim=1
    set COMP_INT=%%R
    set COMP_FLT=%%S
    set COMP_XW=%%T
    set COMP_YW=%%U
  )
)

if %siERROR%==1 (
  echo Error in %0
  exit /B 1
)

rem set /A COMP_XW+=%OFFSET_X%
rem set /A COMP_YW+=%OFFSET_Y%

if "%siDEBUG%"=="1" (
  set /A endX=%COMP_XW%+%subW%-1
  set /A endY=%COMP_YW%+%subH%-1

  rem FIXME: does draw respect offsets? Add +repage to make this irrelevant.

  %IM%convert ^
    %SRC% ^
    +repage ^
    %siDEBUG_STROKE% ^
    -draw "rectangle %COMP_XW%,%COMP_YW% !endX!,!endY!" ^
    %DEBUG_OUT%
)

if not "%fBat%"=="NUL" echo set COMP_XW=%COMP_XW%^&set COMP_YW=%COMP_YW%^&set COMP_FLT=%COMP_FLT%^&set COMP_CROP=%subW%x%subH%+%COMP_XW%+%COMP_YW% >%fBat%

for /F "usebackq" %%L in (`%IM%convert ^
  -ping ^
  %SUB% ^
  -format "centX=%%[fx:int(%COMP_XW%+w/2)]\ncentY=%%[fx:int(%COMP_YW%+h/2)]" ^
  info:`) do set /A %%L

if not "%siDELTEMPSRC%"=="0" (
  call :delTemp %TMPSRC%
)

if not "%siDELTEMPSUB%"=="0" (
  call :delTemp %TMPSUB%
)

if %siERROR%==1 (
  echo Error in %0
  exit /B 1
)

if %IsDissim%==1 (
  set COMP_FLT=1
)

call echoRestore

@endlocal & set siCOMP_XW=%COMP_XW%& set siCOMP_YW=%COMP_YW%& set siCOMP_FLT=%COMP_FLT%& set siIS_DISSIM=%IsDissim%& set siCENT_X=%centX%& set siCENT_Y=%centY%& set siCOMP_CROP=%subW%x%subH%+%COMP_XW%+%COMP_YW%

@exit /B 0



rem ------ Subroutines ----------------------------------------------------------


:delTemp

if /I "%~x1"==".mpc" del %~dpn1.cache 2>nul
del %1 2>nul

exit /B 0


:ResizeSrch
rem %1 is percentage reduction, eg 50 for 50%. May be <1.
rem %2 is multiplier (=100/%1)

echo %0: %1 %2

rem The convert and compare could be combined into one convert.

set COMP_XW=0
set COMP_YW=0

set TMPSRC_RES=%SRC_PREF%_%1%TMPEXT%

@rem FIXME: +repage, then add offsets (-format "%X %Y") at end.
if not "%siDELTEMPSRC%"=="0" (
  del %TMPSRC_RES% 2>nul
)
if not exist %TMPSRC_RES% (
  echo %0: Creating TMPSRC_RES [%TMPSRC_RES%]
  %IM%convert %SRC% +repage -resize %1%% %TMPSRC_RES%
  if ERRORLEVEL 1 set siERROR=1
)

%IM%convert %SUB% -resize %1%% %TMPSUB%

if ERRORLEVEL 1 set siERROR=1

set COMP_XW=

FOR /F "tokens=1-4 usebackq delims=()@, " %%R ^
IN (`%IM%compare ^
    %SIM_DISSIM% -subimage-search ^
    -metric %siMETRIC% %TMPSRC_RES% %TMPSUB% NULL: 2^>^&1`) ^
DO (
    echo %0: %%R %%S %%T %%U
    if /I %%U GTR A set IsDissim=1
    set COMP_INT=%%R
    set COMP_FLT=%%S
    if not "%%T"=="" set /A COMP_XW=%%T*%2
    if not "%%U"=="" set /A COMP_YW=%%U*%2
  )

if "%COMP_XW%"=="" (
  set siERROR=1
  exit /B 1
)

if not "%siDELTEMPSRC%"=="0" (
  del %TMPSRC_RES% 2>nul
)

echo %0:   COMP_XW,YW = %COMP_XW%,%COMP_YW% IsDissim=%IsDissim%

exit /B 0


:CropSrch
rem %1,%2 are coords for top-left of estimated result.
rem %3 is plus or minus for tolerance in both directions.

echo %0: %1,%2 %3

set /A cropL=%1-%3
set /A cropT=%2-%3
if %cropL% LSS 0 set cropL=0
if %cropT% LSS 0 set cropT=0
set /A cropW=%subW%+%3+%3
set /A cropH=%subH%+%3+%3

set COMP_XW=%cropL%
set COMP_YW=%cropT%

if %IsDissim%==1 exit /B 0

%IM%convert ^
  %SRC% ^
  +repage ^
  -crop %cropW%x%cropH%+%cropL%+%cropT% ^
  +repage ^
  %TMPSRC%

if ERRORLEVEL 1 (
  set siERROR=1
  exit /B 1
)

set COMP_INT=

for /F "tokens=1-4 usebackq delims=()@, " %%R ^
in (`%IM%compare ^
    %SIM_DISSIM% -subimage-search ^
    -metric %siMETRIC% %TMPSRC% %SUB% NULL: 2^>^&1`) ^
do (
    echo %0: %%R %%S %%T %%U
    if /I %%U GTR A set IsDissim=1
    set COMP_INT=%%R
    set COMP_FLT=%%S
    if not "%%T"=="" set /A COMP_XW+=%%T
    if not "%%U"=="" set /A COMP_YW+=%%U
)

if "%COMP_INT%"=="" set siERROR=1

echo %0:   COMP_XW,YW = %COMP_XW%,%COMP_YW%   IsDissim=%IsDissim%

exit /B 0


:CropResizeSrch
rem %1,%2 are coords for top-left of estimated result.
rem %3 is plus or minus for tolerance in both directions.
rem %4 is percentage reduction, eg 50 for 50%. May be <1.
rem %5 is multiplier (=100/%4)
rem %1 to %3 are in terms of original full size.

echo %0: %1,%2 %3 %4 %5

set /A cropL=%1-%3
set /A cropT=%2-%3
if %cropL% LSS 0 set cropL=0
if %cropT% LSS 0 set cropT=0
set /A cropW=%subW%+%3+%3
set /A cropH=%subH%+%3+%3

set COMP_XW=%cropL%
set COMP_YW=%cropT%

if %IsDissim%==1 exit /B 0

%IM%convert ^
  %SRC% ^
    -crop %cropW%x%cropH%+%cropL%+%cropT% ^
    +repage ^
    -resize %4%% -write %TMPSRC% +delete ^
  %SUB% -resize %4%% %TMPSUB%

if ERRORLEVEL 1 (
  set siERROR=1
  exit /B 1
)

set COMP_INT=

for /F "tokens=1-4 usebackq delims=()@, " %%R ^
in (`%IM%compare ^
    %SIM_DISSIM% -subimage-search ^
    -metric %siMETRIC% %TMPSRC% %TMPSUB% NULL: 2^>^&1`) ^
do (
    echo %0: %%R %%S %%T %%U
    if /I %%U GTR A set IsDissim=1
    set COMP_INT=%%R
    set COMP_FLT=%%S
    if not "%%T"=="" set /A COMP_XW+=%%T*%5
    if not "%%U"=="" set /A COMP_YW+=%%U*%5
)

if "%COMP_INT%"=="" set siERROR=1

echo %0:   COMP_XW,YW = %COMP_XW%,%COMP_YW%   IsDissim=%IsDissim%

exit /B 0

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

%IM%identify -version
Version: ImageMagick 6.9.5-3 Q16 x86 2016-07-22 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Visual C++: 180040629
Features: Cipher DPC Modules OpenMP 
Delegates (built-in): bzlib cairo flif freetype jng jp2 jpeg lcms lqr openexr pangocairo png ps rsvg tiff webp xml zlib

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


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 4-June-2014.

Page created 15-Jul-2017 13:37:26.

Copyright © 2017 Alan Gibson.