﻿

# What scale?

Given one image that could be another image rescaled, what is the best scale factor?

The problem is solved by trial and error, with a twist: we construct a weird transformation of each image, then use IM's "subimage-search" to find the answer.

This is a sub-problem of the more general problem: "What areas in image A match what areas in image B?"

## Sample inputs

We crop a photograph and make three versions:

1. No resize.
2. Enlarged to 120%.
3. Reduced to 80%.
 ```set wsDEBUG=1 %IM%convert ^ %PICTLIB%20140430\GOPR0166.JPG ^ +depth ^ -crop 600x400+2164+2240 +repage ^ -gravity Center ^ ( -clone 0 ^ -crop 300x200+0+0 +repage ^ -write ws_src1.png ^ +delete ) ^ ( -clone 0 ^ -resize 120%% ^ -crop 300x200+0+0 +repage ^ -write ws_src2.png ^ +delete ) ^ ( -clone 0 ^ -resize 80%% ^ -crop 300x200+0+0 +repage ^ -write ws_src3.png ^ +delete ) ^ NULL:```   We will find what scaling factors applied to the first image would make each of the other two. If the method works, we will get 120% and 80%.

## The method

The images must be the same size. We assume the images with respect to each other are centred and not rotated. (The method can be adapted to allow for rotations. See What rotation and scale?.)

Suppose we crop both input images to the central row, so we have two images, both one pixel high. They won't be exactly equal, because one is a scaled version of the other.

We can rescale the central row by a number of different factors, and append these rows vertically. Now we have a 2-dimensional image that represents the central row of the first image at different scales. If we search for the central row of the second within this 2-D image, the best match will tell us the scale factor.

This would find the best scale for just the central rows, but these might contain weird data that gave a spurious result. We could take every single row and append them together, creating a very wide image that was one pixel high. That should be very accurate but would take a long time and the image would be huge.

The script makes an average of all the rows, by -scale "x1^!". In case this data is weird, it also takes the average of all the columns (by scaling and rotating), and appends this to the average of the rows. The result is necessarily blurred, so it also appends the central row and central column.

By doing this many times, by default 100 times, we get an image that is 2*(W+H) pixels wide and 100 pixels high. This represents the first image at 100 different scales.

The second image is given the same initial treatment but not at different scales. This is 2*(W+H) pixels wide and one pixel high.

Now we search for the second with the first, and the Y-offset gives us the scale. The images are the same width, so the search is fast.

Finally, we obtain a score by scaling the first image, cropping the largest image to the size of the smallest, and comparing.

## Using the script

```call %PICTBAT%whatScale ws_src1.png ws_src2.png
if ERRORLEVEL 1 exit /B 1

echo SCALE=%wsSCALE% FACT=%wsFACT% SCORE_SUB=%wsSCORE_SUB% DODGY=%wsDODGY% ```
`SCALE=0.998978955 FACT=1.01104669 SCORE_SUB=0.123618 DODGY=0 `
```call %PICTBAT%whatScale ws_src1.png ws_src3.png
if ERRORLEVEL 1 exit /B 1

echo SCALE=%wsSCALE% FACT=%wsFACT% SCORE_SUB=%wsSCORE_SUB% DODGY=%wsDODGY% ```
`SCALE=1.01001437 FACT=1.01104669 SCORE_SUB=0.127925 DODGY=0 `

The scale results are very close to the expected values, better than 1%. The "wsFACT" number is an error boundary; the true best result should be between wsSCALE/wsFACT and wsSCALE*wsFACT.

If the returned value wsDODGY was 1, this would mean the best result was at one end of the searched range, suggesting that the true best result was outside the searched range.

If greater precision is required, the script can be called repeatedly with smaller gaps between the tested limits by setting argument 3 to again.

```call %PICTBAT%whatScale ws_src1.png ws_src3.png again
if ERRORLEVEL 1 exit /B 1

echo SCALE=%wsSCALE% FACT=%wsFACT% SCORE_SUB=%wsSCORE_SUB% DODGY=%wsDODGY% ```
`SCALE=1.0153545 FACT=1.00043954 SCORE_SUB=0.128562 DODGY=0 `

The result isn't within the FACT limits. The "again" mechanism searches within the square of FACT. Let's try another "again":

```call %PICTBAT%whatScale ws_src1.png ws_src3.png again
if ERRORLEVEL 1 exit /B 1

echo SCALE=%wsSCALE% FACT=%wsFACT% SCORE_SUB=%wsSCORE_SUB% DODGY=%wsDODGY% ```
`SCALE=1.01551528 FACT=1.00001758 SCORE_SUB=0.128586 DODGY=0 `

As the images are smaller than 1000x1000 pixels, we shouldn't expect a precision or accuracy better than about 0.001.

By default, the script tries 100 scales. It may be quicker to first try just 10, then 20 within the two limits.

These are images that were compared:

 Multi-scale version of the first image, with a blue line showing where the second image was found, and a red line showing where the third image was found: ```%IM%convert ^ %TEMP%\ws_src1_ws_0.5_1.5_100.miff ^ -draw "fill Blue rectangle 0,79 999,79" ^ -draw "fill Red rectangle 0,42 999,42" ^ ws_comp1.png``` The second image, scaled vertically so we can see it: ```%IM%convert ^ %TEMP%\ws_src2_ws.miff ^ -scale "x50^!" ^ ws_comp2.png``` The third image, scaled vertically so we can see it: ```%IM%convert ^ %TEMP%\ws_src3_ws.miff ^ -scale "x50^!" ^ ws_comp3.png``` The multi-scale version of the first image suggests a quick-and-dirty improvement: this could be constructed by making the top and bottom rows and interpolating between them with "-morph". This would give a linear interpolation between the scales instead of a constant ratio.

The images also show that the left halves (which represent the entire image) have substantially less contrast than the right halves (which represent just the central row and column). This may give too much weight to the central row and column, and it might be better to reduce the contrast of these.

It might be better if virtual pixels were made transparent, and we compared with RMSE that ignored transparent pixels.

Unlike What rotation? and What rotation and scale?, all the pixels are used to find the solution.

The debugging images show:

• the original outline of the first image in red;
• the original outline of the second (searched) image in green;
• the crop of the first image, which is also the area used for the score, in yellow.  We can also test the identity case, ie match an image against itself:

```call %PICTBAT%whatScale ws_src1.png ws_src1.png
if ERRORLEVEL 1 exit /B 1

echo SCALE=%wsSCALE% FACT=%wsFACT% SCORE_SUB=%wsSCORE_SUB% DODGY=%wsDODGY% ```
`SCALE=0.998978955 FACT=1.01104669 SCORE_SUB=0.0144762 DODGY=0 `
```call %PICTBAT%whatScale ws_src1.png ws_src1.png again
if ERRORLEVEL 1 exit /B 1

echo SCALE=%wsSCALE% FACT=%wsFACT% SCORE_SUB=%wsSCORE_SUB% DODGY=%wsDODGY% ```
`SCALE=0.999857259 FACT=1.00043954 SCORE_SUB=0.00522144 DODGY=0 `

## Iterating for precision

For large images, a high precision may be needed. The script whatScalePrec.bat iterates until the precision is achieved. It takes the same parameters as whatScale.bat, with one extra parameter, the desired precision eg 1.0001. This might be 1 + 1/max(width,height). A small number of steps should generally be chosen, eg 10.

The script whatScalePrec.bat makes no debugging images. If a debug image is desired, do this after whatScalePrec.bat:

```set wsDEBUG=1
call %PICTBAT%whatScale ws_src1.png ws_src2.png again . 10```

Example use of whatScalePrec.bat:

```for /F "usebackq" %%L ^
in (`%IM%identify ^
-format "PREC=%%[fx:1+1/max(w,h)]" ws_src1.png`) ^
do set %%L

call %PICTBAT%whatScalePrec ws_src1.png ws_src2.png . . 10 %PREC%
if ERRORLEVEL 1 exit /B 1

echo PREC=%PREC%  SCALE=%wspSCALE% FACT=%wspFACT% DODGY=%wspDODGY% ```
`PREC=1.00333  SCALE=1.0029379 FACT=1.00281641 DODGY=0 `

## Alternative: simple trial and error

Some difficult images (eg from a microscope) have most of the image out of focus. For these, a brute force trial-and-error method may be better. This is implemented in whatScaleT.bat (with a "T").

```call %PICTBAT%whatScaleT ws_src1.png ws_src2.png

echo wstSCALE=%wstSCALE% wstCOMP=%wstCOMP% ```
`wstSCALE=1.013689977050685 wstCOMP=0.121792 `

This method assumes there is no substantial rotation.

## Another alternative: concentric rings

Another alternative is to unroll an image ("distort DePolar") scale horizontally to a single column, and rotate that by -90° to get a single row. The pixels in that row then represent the mean colours at different radii from the centre, invariant to rotation. (This is the "Cifi, circular sampling filter" in the Hae Yong Kim paper.)

That's all we do for one image. For the other image, we first resize at various scales, then make a single row for each, then append those vertically. The result represents the image at multiple scales, invariant to rotation. (A quicker method to make this result is to resize the image once, at the maximum scale, and make the single row. Then we resize that row for all the other scales.)

 ```call %PICTBAT%avgConcRings ^ toes.png ws_crings.png``` Now search for the single row that represents one image in the taller image that represents the other image. The y-offset gives us the scale, invariant to rotation. (With a suitable low-level comparison such as my rmsealpha.inc, it can also be invariant to brightness and contrast.)

This method can be developed to find the locations and scales of a subimage in a large image, invariant to rotations. Each candidate location in the large image is cropped, unrolled and scaled, and searched for within the multi-scale of the subimage. With the information loss this inevitably gives false positives, so we then do a second more careful search of only the most promising candidate locations, either by thresholding the scores, or by only searching at locations where the score was a peak.

Calculating the concentric rings is a fairly expensive operation. The method can be first performed on small-scale versions of the images, and the most promising locations searched more carefully.

## Scripts

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

### whatScale.bat

```@rem Given same-size images %1 and %2,
@rem finds scale S in "-distort SRT S,0" for %1 to best match %2, and a score.

@rem Parameters:
@rem   %3 min scale (default 0.5) OR "again" to iterate from previous run.
@rem   %4 max scale (default 1.5)
@rem   %5 number of scale steps (default 100)
@rem   %6 CX } Centre for scaling
@rem   %7 CY }
@rem
@rem Also uses:
@rem   wsDO_SCORE_SUB  if ==0, wsSCORE_SUB will not be found.
@rem   wsMETRIC        default RMSE
@rem   wsDEBUG         if 1, creates images for debugging
@rem
@rem Returns:
@rem   wsDODGY     0 if no problem
@rem   wsSCALE     the found scale factor
@rem   wsFACT      error margin, multiple of wsSCALE
@rem   wsSCORE     (0.0 to 1.0, more or less).
@rem   wsSCORE_SUB (0.0 to 1.0, more or less).
@rem
@rem Revised:
@rem   18-August-2016 Tidied up slightly.
@rem   3-May-2018 Added CX and CY.

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

@setlocal enabledelayedexpansion

rem @call echoOffSave

if "%wsMETRIC%"=="" set wsMETRIC=RMSE

if "%wsDO_SCORE_SUB%"=="" set wsDO_SCORE_SUB=1

set wsMIN_SCALE=%3
if "%wsMIN_SCALE%"=="." set wsMIN_SCALE=
if "%wsMIN_SCALE%"=="" set wsMIN_SCALE=0.5

set wsMAX_SCALE=%4
if "%wsMAX_SCALE%"=="." set wsMAX_SCALE=
if "%wsMAX_SCALE%"=="" set wsMAX_SCALE=1.5

set wsnSTEPS=%5
if "%wsnSTEPS%"=="." set wsnSTEPS=
if "%wsnSTEPS%"=="" set wsnSTEPS=100

set CX=%6
if "%CX%"=="." set CX=
if "%CX%"=="" set CX=0

set CY=%7
if "%CY%"=="." set CY=
if "%CY%"=="" set CY=0

echo CX=%CX%  CY=%CY%

if /I "%wsMIN_SCALE%" EQU "again" (
for /F "usebackq" %%L in (`%IM%identify ^
-precision 9 ^
-format "wsMIN_SCALE=%%[fx:%wsSCALE%/(%wsFACT%*%wsFACT%)]\nwsMAX_SCALE=%%[fx:%wsSCALE%*(%wsFACT%*%wsFACT%)]" ^
xc:`) do set %%L
)

if "%wsMIN_SCALE%"=="" exit /B 1

set TMPEXT=.miff

call %PICTBAT%setInOut %1 ws
set IN_A=%INFILE%
set TEMP_A_BASE=%TEMP%\%~n1_ws
set TEMP_A=%TEMP%\%~n1_ws_%wsMIN_SCALE%_%wsMAX_SCALE%_%wsnSTEPS%%TMPEXT%

set IN_A_TMP=%TEMP%\%~n1_ws_inA%TMPEXT%

call %PICTBAT%setInOut %2 ws
set IN_B=%INFILE%
set TEMP_B=%TEMP%\%~n2_ws%TMPEXT%
if %IN_B%==%IN_A% set TEMP_B=%TEMP%\%~n2_B_ws%TMPEXT%

set DEBUG_FILE=ws_%~n1_%~n2_dbg%EXT%

echo IN_A=%IN_A% IN_B=%IN_B%

%IM%convert %IN_A% %IN_A_TMP%

for /F "usebackq" %%L ^
in (`%IM%identify -format "WW1=%%w\nHH1=%%h" %IN_A_TMP%`) ^
do set %%L

if "%WW1%"=="" exit /B 1

for /F "usebackq" %%L ^
in (`%IM%identify ^
-precision 9 ^
-format "FACT=%%[fx:pow(%wsMAX_SCALE%/%wsMIN_SCALE%,1/%wsnSTEPS%)]" ^
xc:`) ^
do set %%L

for /F "usebackq" %%L ^
in (`%IM%identify ^
-precision 9 ^
-format "FINISHED=%%[fx:abs(%FACT%-1)<0.0000001]" ^
xc:`) ^
do set %%L

if "%FINISHED%"=="1" (
set SCALE=%wsMIN_SCALE%

for /F "usebackq" %%L ^
in (`%IM%identify ^
-precision 9 ^
-format "SCALE=%%[fx:sqrt(%wsMIN_SCALE%*%wsMAX_SCALE%)]" ^
xc:`) ^
do set %%L

set DODGY=0
goto end
)

if exist %TEMP_A% goto skipTempA

set SCALE=%wsMIN_SCALE%
set NAMES_A=
for /L %%i in (1,1,%wsnSTEPS%) do (
@rem Maybe we should crop all scales so we never have virtual pixels.

%IM%convert ^
%IN_A_TMP% ^
-distort SRT %CX%,%CY%,!SCALE!,0 ^
-gravity Center ^
-virtual-pixel Mirror ^
-define distort:viewport=%WW1%x%HH1% ^
^( -clone 0 -scale "x1^!" ^
-define distort:viewport=%WW1%x1 -distort SRT 1,0 +repage ^) ^
^( -clone 0 -scale "1x^!" ^
-define distort:viewport=1x%HH1% -distort SRT 1,0 +repage ^
-rotate 90 +repage ^) ^
^( -clone 0 -crop %WW1%x1+0+0 +repage ^) ^
^( -clone 0 -crop 1x%HH1%+0+0 +repage -rotate 90 +repage ^) ^
-delete 0 ^
+append +repage ^
%TEMP_A_BASE%_%%i%TMPEXT%

set NAMES_A=!NAMES_A! %TEMP_A_BASE%_%%i%TMPEXT%

for /F "usebackq" %%L in (`%IM%identify ^
-precision 19 -format "SCALE=%%[fx:!SCALE!*%FACT%]" ^
xc:`) do set %%L
)

rem echo NAMES_A=%NAMES_A%

%IM%convert %NAMES_A% -append +repage %TEMP_A%

:skipTempA

rem %IM%identify %TEMP_A%

if not exist %TEMP_B% %IM%convert ^
%IN_B% ^
-gravity Center ^
-virtual-pixel Mirror ^
^( -clone 0 -scale "x1^!" ^) ^
^( -clone 0 -scale "1x^!" -rotate 90 +repage ^) ^
^( -clone 0 -crop %WW1%x1+0+0 +repage ^) ^
^( -clone 0 -crop 1x%HH1%+0+0 +repage -rotate 90 +repage ^) ^
-delete 0 ^
+append +repage ^
%TEMP_B%

rem %IM%identify %TEMP_B%

for /F "usebackq tokens=1-4 delims=()@, " %%R ^
in (`%IM%compare ^
%TEMP_A% ^
%TEMP_B% ^
-similarity-threshold 0 -dissimilarity-threshold 1 -subimage-search ^
-metric %wsMETRIC% ^
NULL: 2^>^&1`) ^
do (
@set COMP_INT=%%R
@set COMP_FLT=%%S
@set COMP_XW=%%T
@set COMP_YW=%%U
)

echo COMP_FLT=%COMP_FLT% COMP_XW=%COMP_XW% COMP_YW=%COMP_YW%

if "%COMP_FLT%" GTR "9" exit /B 1

set DODGY=0
if %COMP_YW% EQU 0 set DODGY=1
if %COMP_YW% GEQ %wsnSTEPS% set DODGY=1

rem What is the magnification here?
set SCALE=%wsMIN_SCALE%
for /L %%i in (1,1,%COMP_YW%) do (
for /F "usebackq" %%L in (`%IM%identify ^
-precision 9 -format "SCALE=%%[fx:!SCALE!*%FACT%]" ^
xc:`) do set %%L
)

rem echo SCALE=%SCALE%

rem Cleanup: remove most temporary files.
for /L %%i in (1,1,%wsnSTEPS%) do (
@del %TEMP_A_BASE%_%%i%TMPEXT% 2>nul
)

:end

if %wsDO_SCORE_SUB%==1 (
rem Test result.

for /F "usebackq" %%L in (`%IM%identify ^
-format "cropX=%%[fx:int(min(%WW1%,%WW1%*%SCALE%))]\ncropY=%%[fx:int(min(%HH1%,%HH1%*%SCALE%))]" ^
xc:`) do set %%L

set ScoreSub=

for /F "usebackq" %%L in (`%IM%convert ^
%IN_A_TMP% ^
-distort SRT "%CX%,%CY%,%SCALE%,0" ^
%IN_B% ^
-gravity Center ^
-crop !cropX!x!cropY!+0+0 +repage ^
-metric %wsMETRIC% -format "ScoreSub=%%[distortion]" -compare ^
info:`) do set %%L

if "!ScoreSub!"=="" set DODGY=1
)

if "%wsDEBUG%"=="1" (

for /F "usebackq" %%L in (`%IM%identify ^
-format "cropX=%%[fx:int(min(%WW1%,%WW1%*%SCALE%))]\ncropY=%%[fx:int(min(%HH1%,%HH1%*%SCALE%))]" ^
xc:`) do set %%L

%IM%convert ^
%IN_A_TMP% ^
-virtual-pixel None ^
^( %IN_B% ^
^( +clone -alpha Transparent ^
-shave 1x1 ^
-bordercolor None ^
-mattecolor #0f0 -frame 1x1 ^
-channel A -evaluate Multiply 0.75 ^
^) ^
-composite ^
^) ^
^( -clone -1 ^
-gravity Center ^
-crop !cropX!x!cropY!+0+0 ^
-alpha Transparent ^
-shave 1x1 ^
-bordercolor None ^
-mattecolor #ff0 -frame 1x1 ^
-channel A -evaluate Multiply 0.75 ^
^) ^
^( -clone 0 -alpha Transparent ^
-shave 1x1 ^
-bordercolor None ^
-mattecolor #f00 -frame 1x1 ^
-channel A -evaluate Multiply 0.75 ^
^) ^
-layers merge ^
+repage ^
%DEBUG_FILE%

if ERRORLEVEL 1 exit /B 1
)

call echoRestore

endlocal & set wsSCALE=%SCALE%& set wsFACT=%FACT%& set wsSCORE=%COMP_FLT%& set wsSCORE_SUB=%ScoreSub%& set wsDODGY=%DODGY%```

### whatScalePrec.bat

```@rem Iterates whatScale.bat until required precision.
@rem
@rem Given same-size images %1 and %2,
@rem finds scale S in "-distort SRT S,0" for %1 to best match %2, and a score.

@rem Parameters:
@rem   %3 min scale (default 0.5)
@rem   %4 max scale (default 1.5)
@rem   %5 number of scale steps (default 100)
@rem   %6 iterates until wsFACT <= %6, eg 1.0001

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

@setlocal

@call echoOffSave

call %PICTBAT%whatScale %1 %2 %3 %4 %5
if not %wsDODGY%==0 (
set wspDODGY=1
exit /B 1
)

set wsDO_SCORE_SUB=0
set wsDEBUG=

:loop

set FINISHED=
for /F "usebackq" %%L ^
in (`%IM%identify -format "FINISHED=%%[fx:%wsFACT%<=%6?1:0]" xc:`) ^
do set %%L

if not %FINISHED%==1 (
call %PICTBAT%whatScale %1 %2 again . %5
echo wsSCALE=!wsSCALE!
if not !wsDODGY!==0 (
set wspDODGY=1
exit /B 1
)

goto loop
)

call echoRestore

endlocal & set wspSCALE=%wsSCALE%& set wspFACT=%wsFACT%& set wspSCORE=%wsSCORE%& set wspDODGY=%wsDODGY%```

### whatScaleT.bat

```@rem Given same-size images %1 and %2,
@rem finds scale S in "-distort SRT S,0" for %1 to best match %2, and a score.
@rem
@rem Parameters:
@rem   %3 min scale (default 0.5)
@rem   %4 max scale (default 1.5)
@rem   %5 CX } Centre for scaling
@rem   %6 CY }

@rem Unlike whatScale.bat,
@rem this works by brute force trial-and-error of the entire images.
@rem So this may be more suitable when not much of either image is in focus.

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

@setlocal enabledelayedexpansion

@call echoOffSave

if "%wstMETRIC%"=="" set wstMETRIC=RMSE

set wstMIN_SCALE=%3
if "%wstMIN_SCALE%"=="." set wstMIN_SCALE=
if "%wstMIN_SCALE%"=="" set wstMIN_SCALE=0.5

set wstMAX_SCALE=%4
if "%wstMAX_SCALE%"=="." set wstMAX_SCALE=
if "%wstMAX_SCALE%"=="" set wstMAX_SCALE=1.5

if "%wstMIN_SCALE%"=="" exit /B 1

set CX=%5
if "%CX%"=="." set CX=
if "%CX%"=="" set CX=0

set CY=%6
if "%CY%"=="." set CY=
if "%CY%"=="" set CY=0

echo CX=%CX%  CY=%CY%

set PRECISION=%wstPRECISION%
if "%PRECISION%"=="" set PRECISION=-precision 6

set TMPEXT=.miff
set TMPDIR=%TEMP%

call %PICTBAT%setInOut %1 wst
set IN_A=%INFILE%
set IN_A_TMP=%TMPDIR%\%~n1_wst_inA%TMPEXT%

call %PICTBAT%setInOut %2 wst
set IN_B=%INFILE%
set IN_B_TMP=%TMPDIR%\%~n2_wst_inB%TMPEXT%

set DEBUG_FILE=wst_%~n1_%~n2_dbg%EXT%

echo IN_A=%IN_A%  IN_B=%IN_B%

if "%~x1"=="%TMPEXT%" (
set IN_A_TMP=%IN_A%
) else (
%IM%convert %IN_A% %IN_A_TMP%
)

if "%~x2"=="%TMPEXT%" (
set IN_B_TMP=%IN_B%
) else (
%IM%convert %IN_B% %IN_B_TMP%
)

for /F "usebackq" %%L ^
in (`%IM%identify ^
-precision 16 ^
-format "WW1=%%w\nHH1=%%h\nInvCX=%%[fx:-(%CX%)]\nInvCY=%%[fx:-(%CY%)]" ^
%IN_A_TMP%`) ^
do set %%L

if "%WW1%"=="" exit /B 1

echo InvCX=%InvCX%  InvCY=%InvCY%

set SCALE0=%wstMIN_SCALE%
set SCALE1=0
set SCALE2=0
set SCALE3=%wstMAX_SCALE%

set A_SCALE=%TMPDIR%\%~n1_wst_A_S%TMPEXT%
set B_SCALE=%TMPDIR%\%~n2_wst_B_S%TMPEXT%

call :TryScale %SCALE0%
set COMP0=!COMP!

call :TryScale %SCALE3%
set COMP3=!COMP!

set nIter=0
set FINISHED=0

:loop

if %SCALE0%==%SCALE2% set FINISHED=1

for /F "usebackq" %%L ^
in (`%IM%identify %PRECISION% ^
-precision 16 ^
-format "SCALE1=%%[fx:%SCALE0%*pow(%SCALE3%/%SCALE0%,1/3)]\nSCALE2=%%[fx:%SCALE0%*pow(%SCALE3%/%SCALE0%,2/3)]" ^
xc:`) ^
do set %%L

if %SCALE0%==%SCALE1% set FINISHED=1
if %SCALE1%==%SCALE2% set FINISHED=1
if %SCALE2%==%SCALE3% set FINISHED=1

call :TryScale %SCALE1%
set COMP1=!COMP!

call :TryScale %SCALE2%
set COMP2=!COMP!

echo Scale: %SCALE0% %SCALE1% %SCALE2% %SCALE3%
echo Comp: %COMP0% %COMP1% %COMP2% %COMP3%

for /F "usebackq" %%L ^
in (`%IM%identify ^
-format "High0=%%[fx:%COMP0%>%COMP3%?1:0]\nHigh3=%%[fx:%COMP0%<%COMP3%?1:0]" ^
xc:`) ^
do set %%L

if %High0%==1 (
set SCALE0=%SCALE1%
set COMP0=%COMP1%
) else if %High3%==1 (
set SCALE3=%SCALE2%
set COMP3=%COMP2%
) else (
set FINISHED=1
)

set /A nIter+=1

if %FINISHED%==0 goto loop

echo nIter=%nIter%

call echoRestore

endlocal & set wstSCALE=%SCALE1%& set wstCOMP=%COMP1%

exit /B 0

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

:TryScale
set SC=%1

for /F "usebackq" %%L ^
in (`%IM%identify ^
-precision 16 ^
-format "Gtr1=%%[fx:%SC%>1?1:0]\nInvSc=%%[fx:1.0/%SC%]" ^
xc:`) ^
do set %%L

if %Gtr1%==1 (

%IM%convert ^
%IN_A_TMP% ^
-distort SRT %CX%,%CY%,%SC%,0 ^
%A_SCALE%

for /F "tokens=2 usebackq delims=() " %%L ^
in (`%IM%compare ^
%A_SCALE% ^
%IN_B_TMP% ^
-metric %wstMETRIC% ^
NULL: 2^>^&1`) ^
do set COMP=%%L

) else (
%IM%convert ^
%IN_B_TMP% ^
-distort SRT %InvCX%,%InvCY%,%InvSc%,0 ^
%B_SCALE%

for /F "tokens=2 usebackq delims=() " %%L ^
in (`%IM%compare ^
%IN_A_TMP% ^
%B_SCALE% ^
-metric %wstMETRIC% ^
NULL: 2^>^&1`) ^
do set COMP=%%L

)
exit /B 0```

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

`%IM%identify -version`
```Version: ImageMagick 6.9.9-50 Q16 x64 2018-06-02 http://www.imagemagick.org
Visual C++: 180040629
Features: Cipher DPC Modules OpenMP
Delegates (built-in): bzlib cairo flif freetype gslib heic jng jp2 jpeg lcms lqr lzma openexr pangocairo png ps raw rsvg tiff webp xml zlib```

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

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

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.