﻿

# Shape to shape

We can distort a shape to match another shape.

The general problem is to distort an arbitrary shape into another arbitrary shape.

Some commands and scripts on this page use those process modules. Without those modules, the commands won't work.

## Sample input

We create a sample source, with a yellow grid and red boundary to clearly show the distortions.

 ```set SRC=s2s_src.png %IMG7%magick ^ %PICTLIB%20140430\GOPR0180.jpg ^ -resize 400x400 ^ %SRC% for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "WW=%%w\nHH=%%h\nWm1=%%[fx:w-1]\nHm1=%%[fx:h-1]" ^ %SRC%`) do set %%L call %PICTBAT%grid %WW% %HH% 6 4 1 yellow none red %IMG7%magick ^ %SRC% ^ grid.png ^ -composite ^ %SRC%```

## Using "-fx"

If the required shape is defined mathematically, we can distort it with "-fx". Fx is slow, but accurate and easy to use.

The examples below use a number of fx built-in variables or "symbols". See the official page Apply Special Effects to an Image with an Fx Expression.

Symbol Meaning
i column offset: 0, 1, ... w-1
j row offset: 0, 1, ... h-1
w width; number of columns
h height; number of rows

My own variables use capital letters. For efficiency, we pre-calculate w/2 and h/2:

```for /F "usebackq" %%L in (`%IMG7%magick identify ^
-format "W_2=%%[fx:w/2]\nH_2=%%[fx:h/2]" ^
%SRC%`) do set %%L```

Examples, with horizontal displacement only:

 No-op ```%IMG7%magick ^ %SRC% ^ -fx "p{i,j}" ^ s2s_fx0.png``` Triangle ```%IMG7%magick ^ %SRC% ^ -fx ^ p{j==0?0:i*h/j,j} ^ s2s_fx1.png``` Triangle ```%IMG7%magick ^ %SRC% ^ -fx ^ p{(j==0?0:((i/w-0.5)*h/j+0.5)*w),j} ^ s2s_fx2.png``` Diamond ```%IMG7%magick ^ %SRC% ^ -fx ^ JH=(j^>%H_2%)?h-j-1:j;^ p{JH==0?0:((i/w-0.5)*h/JH+0.5)*w,j} ^ s2s_fx3.png``` Hexagon ```%IMG7%magick ^ %SRC% ^ -fx ^ JH=(j^>%H_2%)?h-j-1:j;^ FF=h/(JH+%H_2%);^ p{((i/w-0.5)*FF+0.5)*w,j} ^ s2s_fx4.png``` If h/w = sqrt(3/4), the hexagon will be regular. We can invert the hexagon transformation, or any other, by dividing by F instead of multiplying. ```%IMG7%magick ^ s2s_fx4.png ^ -fx ^ JH=(j^>%H_2%)?h-j-1:j;^ FF=h/(JH+%H_2%);^ p{((i/w-0.5)/FF+0.5)*w,j} ^ s2s_fx4a.png``` Ellipse ```%IMG7%magick ^ %SRC% ^ -fx ^ JH=(j^>%H_2%)?h-j-1:j;^ JHM=(%H_2%-JH)/h;^ FF=(JH==0)?0:1/(sqrt(1-4*JHM*JHM));^ p{((i/w-0.5)*FF+0.5)*w,j} ^ s2s_fx5.png```

Vertical displacements can be performed by rotation, or adapting the above examples by swapping i and j, and w and h:

 Ellipse by vertical displacement ```%IMG7%magick ^ %SRC% ^ -fx ^ IW=(i^>%W_2%)?w-i-1:i;^ IWM=(%W_2%-IW)/w;^ FF=(IW==0)?0:1/(sqrt(1-4*IWM*IWM));^ p{i,((j/h-0.5)*FF+0.5)*h} ^ s2s_fx6.png```

Some fancy displacements:

 Ellipse, discontinuous at central vertical ```%IMG7%magick ^ %SRC% ^ -fx ^ DX=i/%W_2%-1;^ DY=j/%H_2%-1;^ AA=atan2(DY,DX);^ RR=sqrt(DX*DX+DY*DY);^ FX=abs(cos(AA));^ FX=(FX==0)?0.00001:FX;^ p{((i/w-0.5)/FX+0.5)*w,j} ^ s2s_fx7.png``` Ellipse, discontinuous at central horizontal ```%IMG7%magick ^ %SRC% ^ -fx ^ DX=i/%W_2%-1;^ DY=j/%H_2%-1;^ AA=atan2(DY,DX);^ RR=sqrt(DX*DX+DY*DY);^ FY=abs(sin(AA));^ FY=(FY==0)?0.00001:FY;^ p{i,((j/h-0.5)/FY+0.5)*h} ^ s2s_fx8.png``` Points on the ellipse have no distortion ```%IMG7%magick ^ %SRC% ^ -fx ^ DX=i/%W_2%-1;^ DY=j/%H_2%-1;^ AA=atan2(DY,DX);^ RR=sqrt(DX*DX+DY*DY);^ FX=RR;^ FY=RR;^ p{((i/w-0.5)*FX+0.5)*w,((j/h-0.5)*FY+0.5)*h} ^ s2s_fx9.png``` Angle discontinuity at the diagonals. ```%IMG7%magick ^ %SRC% ^ -fx ^ DX=i/%W_2%-1;^ DY=j/%H_2%-1;^ AA=atan2(DY,DX);^ RS=1/max(abs(cos(AA)),abs(sin(AA)));^ FX=RS;^ FY=RS;^ p{((i/w-0.5)*FX+0.5)*w,((j/h-0.5)*FY+0.5)*h} ^ s2s_fx10.png``` ```%IMG7%magick ^ %SRC% ^ -fx ^ DX=i/%W_2%-1;^ DY=j/%H_2%-1;^ AA=atan2(DY,DX);^ RR=sqrt(DX*DX+DY*DY);^ RS=abs(cos(AA));^ RS=(RS==0)?0.00001:RS;^ FX=RS;^ FY=RS;^ p{((i/w-0.5)/FX+0.5)*w,((j/h-0.5)/FY+0.5)*h} ^ s2s_fx11.png``` ```%IMG7%magick ^ %SRC% ^ -fx ^ DX=i/%W_2%-1;^ DY=j/%H_2%-1;^ AA=atan2(DY,DX);^ RS=(abs(cos(AA))+abs(sin(AA)))/2;^ RS=(RS==0)?0.00001:RS;^ FX=RS;^ FY=RS;^ p{((i/w-0.5)/FX+0.5)*w,((j/h-0.5)/FY+0.5)*h} ^ s2s_fx13.png``` ```%IMG7%magick ^ %SRC% ^ -fx ^ DX=i/%W_2%-1;^ DY=j/%H_2%-1;^ AA=atan2(DY,DX);^ FX=abs(sin(AA));^ FY=abs(cos(AA));^ FX=(FX==0)?0.00001:FX;^ FY=(FY==0)?0.00001:FY;^ p{((i/w-0.5)/FX+0.5)*w,((j/h-0.5)/FY+0.5)*h} ^ s2s_fx15.png```

## Displacement maps: method

For an abritrary shape, we can make a displacement map from the mask.

For a mask, draw a white shape on a black background. For this example, we ensure the white shape extends to the top and bottom edges. Each horizontal line has contiguous white pixels.

We will distort the source horizontally to fit within the white shape. We do this by creating a relative displacement map that fits within the white shape. The map will be black at the left, white at the right, with a smooth gradient between. This will displace pixels from the left of the source rectangle to the left of the shape, and from the right of the source to the right of the shape.

We will also create the inverse map, so we can displace from the shape back to the source rectangle.

As a first step towards the relative displacement map, we create an absolute displacement map.

Creating the absolute displacement map with "pure" IM operations is messy, but is trivially simple with my cumulhisto process. (See Customising ImageMagick: cumulate histogram.)

 Create the absolute and relative maps: ```%IM7DEV%magick ^ s2s_cent_mask.png ^ +depth ^ -process 'cumulhisto norm' ^ -write s2s_cent_absmap.png ^ ( +clone ^ -sparse-color Bilinear ^ "0,0 Black %%[fx:w-1],0 White" ^ ) ^ -compose Mathematics ^ -define compose:args=0,-0.5,0.5,0.5 ^ -composite ^ s2s_cent_relmap.png``` Apply the relative map, and mask the result down to the original mask: ```%IMG7%magick ^ %SRC% ^ s2s_cent_relmap.png ^ -compose Displace ^ -set option:compose:args %Wm1%x0 -composite ^ s2s_cent_mask.png ^ -alpha off ^ -compose CopyOpacity -composite ^ s2s_cm.png``` Horizontal lines remain straight; vertical lines are distorted.

From the distorted image, we can restore the original rectangular image. We do this by taking the forwards absolute map, inverting it, and converting this to make the relative map:

 Create the inverse absolute and relative maps: ```%IM7DEV%magick ^ s2s_cent_absmap.png ^ +depth ^ -process 'invclut' ^ +write s2s_cent_absmapinv.png ^ ( +clone ^ -sparse-color Bilinear ^ "0,0 Black %%[fx:w-1],0 White" ^ ) ^ -compose Mathematics ^ -define compose:args=0,-0.5,0.5,0.5 ^ -composite ^ s2s_cent_relmapinv.png``` Apply the inverse relative map: ```%IMG7%magick ^ s2s_cm.png ^ s2s_cent_relmapinv.png ^ -compose Displace ^ -set option:compose:args %Wm1%x0 -composite ^ s2s_cminv.png```

The process modules cumulhisto and invclut operate horizontally, treating each line as a clut. If vertical displacement is wanted, we rotate the mask before and after cumulhisto, and we make a gradient from top to bottom instead of left to right.

 Create the rotated absolute and relative maps: ```%IM7DEV%magick ^ s2s_cent_mask.png ^ -rotate -90 ^ -process 'cumulhisto norm' ^ -rotate 90 ^ +depth ^ +write s2s_cent_absmap_r.png ^ ( +clone ^ -sparse-color Bilinear ^ "0,0 Black 0,%%[fx:h-1] White" ^ ) ^ -compose Mathematics ^ -define compose:args=0,-0.5,0.5,0.5 ^ -composite ^ s2s_cent_relmap_r.png``` Apply the rotated relative map as a vertical displacement: ```%IMG7%magick ^ %SRC% ^ s2s_cent_relmap_r.png ^ -virtual-pixel Black ^ -compose Displace ^ -set option:compose:args 0x%Hm1% -composite ^ s2s_cm_r.png``` Vertical lines remain straight; horizontal lines are distorted.

This has trimmed the right and left sides because the mask doesn't extend that far. We can first shrink the source to the width of the mask, then apply the vertical displacement.

 Find the mask dimensions: ```for /F "usebackq tokens=1-6 delims=x+-," %%A ^ in (`%IMG7%magick ^ s2s_cent_mask.png ^ -bordercolor Black -border 1 ^ -format "%%@,%%w,%%h" ^ info:`) ^ do ( set /A TW=%%A set /A TH=%%B set /A TX=%%C-1 set /A TY=%%D-1 set /A WW=%%E-2 set /A HH=%%F-2 ) echo TW=%TW% TH=%TH% TX=%TX% TY=%TY% WW=%WW% HH=%HH%``` `TW=259 TH=300 TX=83 TY=0 WW=400 HH=300` ```%IMG7%magick ^ %SRC% ^ -resize "%TW%x%TH%^!" ^ -background Blue ^ -extent %WW%x%HH%-%TX%-%TY% ^ s2s_cent_relmap_r.png ^ -compose Displace ^ -set option:compose:args 0x%Hm1% -composite ^ s2s_cm_rz.png``` The entire source is contained within the mask. Vertical lines remain straight; horizontal lines are distorted. As before, the result could be masked down to the required shape.

## Displacement maps: script

The above processes can be combined in a script, sh2shLinear.bat. For help, call it with no arguments.

```call %PICTBAT%sh2shLinear

cmd /c exit /B 0```
```@rem Given image %1
@rem and mask %2, white shape on black background,
@rem distorts image into white shape with horizontal or linear displacement.
@rem
@rem Optional arguments:
@rem   %3 direction of displacement
@rem        h=horizontal, v=vertical [h]
@rem   %4 proportion towards displacement (0 to 100) [100]
@rem   %5 reverse transformation (shape -> rectangle) NYI
@rem        f=forwards, r=reverse [f]
@rem   %6 output filename [*_s2sl.*]
@rem        y=yes, n=no [y]
@rem   %8 filename for output relative displacement map [no file]
@rem
@rem Updated:
@rem   27-August-2022 for IM v7.
@rem```
 Horizontal: ```call %PICTBAT%sh2shLinear ^ %SRC% s2s_cent_mask.png``` Vertical: ```call %PICTBAT%sh2shLinear ^ %SRC% s2s_cent_mask.png v . . s2s_linscr2.png``` Horizontal, 75% of effect, without masking-down: ```call %PICTBAT%sh2shLinear ^ %SRC% s2s_cent_mask.png h 75 . s2s_linscr3.png 0``` Horizontal, 75% of effect, reverse, without masking-down: ```call %PICTBAT%sh2shLinear ^ %SRC% s2s_cent_mask.png h 75 r s2s_linscr4.png 0``` Horizontal, 100% of effect, reverse, without masking-down, taking input from the forwards distortion: ```call %PICTBAT%sh2shLinear ^ s2s_src_s2sl.png s2s_cent_mask.png h . r s2s_linscr5.png 0``` As expected, we are back where we started.

## Horizontal stretch method

If we have a shape that we want to stretch horizontally into a rectangle, here is a simple method that doesn't use process modules.

1. Make an identity absolute displacement map the size of the rectangle.
2. Mark as blue the pixels near the right and left edges that are outside the shape.
3. Crop into lines. Trim any blue from the ends of each line. Resize all lines to the full width. Append the lines vertically.
4. Now we have an absolute displacement map that transforms the shape into the bounding rectangle. Apply this map.

The script stretchHoriz.bat does this. Note that, unlike other scripts on this page, it stretches out only to the bounding rectangle of the white pixels in the mask. If the requirement is to stretch to the image rectangle, remove the %MASK_CROP% operations.

 ```call %PICTBAT%stretchHoriz ^ s2s_src_s2sl.png s2s_cent_mask.png s2s_strh.png``` We are back where we started, but with a changed aspect ratio.

Although designed for stretching horizontally, it can instead stretch vertically, by rotations.

The script has provision for supersampling the image and mask, and subsampling at the end, which slightly improves quality. However, a bigger improvement is often available by supersampling the image first and making the mask from that.

## Polar displacement: method

We can unroll both source and mask with depolar, apply a vertical displacement, and roll up the result.

 Unroll the source: ```%IMG7%magick ^ %SRC% ^ -background Blue ^ -virtual-pixel Background ^ -distort depolar -1 ^ s2s_src_dep.png``` Unroll the mask: ```%IMG7%magick ^ s2s_cent_mask.png ^ -background Blue ^ -virtual-pixel Background ^ -distort depolar -1 ^ s2s_mask_dep.png```

Looking at the unrolled mask, we want a vertical displacement map that will move the upper edge of the blue area to the lower edge of the white area. We can do this in two stages. The first stage will expand the image downwards from the top of the blue area to the lower edge of the blue area (which is the bottom edge of the rectangle). The second stage will shrink the image upwards from the lower edge of the blue area to the lower edge of the white area. Each stage needs a relative displacement map.

Stage 1 distorts the source rectangle into a circle (in the same coordinate system). Stage 2 distorts the circle into the required shape.

We can either apply these two maps sequentially (Test stage 1 and Test stage 2 below), or we can combine the two maps, then apply this combined map.

 Stage 1: ```%IM7DEV%magick ^ s2s_cent_mask.png ^ +depth ^ -fill White -colorize 100 ^ -background Black ^ -virtual-pixel Background ^ -distort depolar -1 ^ -rotate -90 ^ +write s2s_mask_dep_st1b.png ^ -process 'cumulhisto norm' ^ -process 'invclut' ^ +write s2s_mask_dep_st1a.png ^ ( +clone ^ -sparse-color Bilinear ^ "0,0 Black %%[fx:w-1],0 White" ^ ) ^ -compose Mathematics ^ -define compose:args=0,-0.5,0.5,0.5 ^ -composite ^ s2s_mask_dep_st1.png``` Test stage 1: ```%IMG7%magick ^ s2s_mask_dep_st1.png ^ ( +clone -sparse-color Bilinear ^ "0,0 Red %%[fx:w-1],0 Lime" ) ^ +swap ^ -background Blue -virtual-pixel Background ^ -compose Displace ^ -set option:compose:args %Wm1%x0 -composite ^ s2s_pol_test1a.png %IMG7%magick ^ s2s_src_dep.png ^ -rotate -90 ^ s2s_mask_dep_st1.png ^ -compose Displace ^ -set option:compose:args %Hm1%x0 -composite ^ s2s_pol_test1b.png``` Stage 2: ```%IM7DEV%magick ^ s2s_cent_mask.png ^ +depth ^ -background Black ^ -virtual-pixel Background ^ -distort depolar -1 ^ -rotate -90 ^ +write s2s_mask_dep_st2b.png ^ -process 'cumulhisto norm' ^ +write s2s_mask_dep_st2a.png ^ ( +clone ^ -sparse-color Bilinear ^ "0,0 Black %%[fx:w-1],0 White" ^ ) ^ -compose Mathematics ^ -define compose:args=0,-0.5,0.5,0.5 ^ -composite ^ s2s_mask_dep_st2.png``` Test stage 2: ```%IMG7%magick ^ s2s_mask_dep_st2.png ^ ( +clone -sparse-color Bilinear ^ "0,0 Red %%[fx:w-1],0 Lime" ) ^ +swap ^ -background Blue -virtual-pixel Background ^ -compose Displace ^ -set option:compose:args %Hm1%x0 -composite ^ s2s_pol_test2a.png %IMG7%magick ^ s2s_pol_test1b.png ^ s2s_mask_dep_st2.png ^ -compose Displace ^ -set option:compose:args %Hm1%x0 -composite ^ s2s_pol_test2b.png %IMG7%magick ^ s2s_pol_test2b.png ^ -rotate 90 ^ -distort polar -1 ^ s2s_pol_test2b_out.png``` Combine the two maps, and rotate: ```%IMG7%magick ^ s2s_mask_dep_st1.png ^ s2s_mask_dep_st2.png ^ -compose Mathematics ^ -define compose:args=0,1,1,-0.5 ^ -composite ^ -rotate 90 ^ s2s_mask_comb12a.png %IMG7%magick ^ s2s_mask_dep_st1.png ^ ( +clone ^ -sparse-color Bilinear ^ "0,0 Black %%[fx:w-1],0 White" ^ -write mpr:IDENT ^ ) ^ +swap ^ -compose Displace ^ -set option:compose:args %Hm1%x0 -composite ^ s2s_mask_dep_st2.png ^ -compose Displace ^ -set option:compose:args %Hm1%x0 -composite ^ mpr:IDENT ^ -compose Mathematics ^ -define compose:args=0,-0.5,0.5,0.5 ^ -composite ^ -rotate 90 ^ s2s_mask_comb12.png``` Apply this rotated combined relative map: ```%IMG7%magick ^ s2s_src_dep.png ^ s2s_mask_comb12.png ^ -compose Displace ^ -set option:compose:args 0x%Hm1% -composite ^ s2s_pol_out.png``` Polar the result: ```%IMG7%magick ^ s2s_pol_out.png ^ -distort polar -1 ^ s2s_pol_out2.png```

One advantage of a combined displacement map is that it enables a simple transition between the source and the distortion, for example 75% towards the distortion.

 : ```%IMG7%magick ^ s2s_mask_comb12.png ^ -fill gray(50%%) -colorize 25 ^ s2s_src_dep.png ^ +swap ^ -compose Displace ^ -set option:compose:args 0x%Hm1% -composite ^ -distort polar -1 ^ s2s_pol_bl_out.png```

## Polar displacement: script

The above processes can be combined in a script, sh2shPolar.bat. For help, call it with no arguments.

```call %PICTBAT%sh2shPolar

cmd /c exit /B 0```
```@rem Given image %1
@rem and mask %2, white shape on black background,
@rem distorts image into white shape with horizontal or polar displacement.
@rem
@rem Optional arguments:
@rem   %3 proportion towards displacement (0 to 100) [100]
@rem   %4 reverse transformation (shape -> rectangle)
@rem        f=forwards, r=reverse (r is buggy; FIXME) [f]
@rem   %5 output filename [*_s2sp.*]
@rem        1=yes, 0=no [1]
@rem   %7 filename for output relative displacement map [no file] (NYI)
@rem
@rem Also uses:
@rem   s2spSUP_SAMP Factor for supersampling [default 1].
@rem
@rem Updated:
@rem   22-August-2022 Upgraded for IM v7.
@rem
@rem FIXME: also need method of specifying central coord.
@rem        (If neither specified, leave blank in "-distort".)
@rem
rem IM bug? if %OUTFILE% is .png, we get corrupt file, unless we have "-depth 16".```
 Default: ```call %PICTBAT%sh2shPolar ^ %SRC% s2s_cent_mask.png``` 75% of effect, no mask: ```call %PICTBAT%sh2shPolar ^ %SRC% s2s_cent_mask.png 75 . s2s_pol2.png 0``` With supersampling: ```set s2spSUP_SAMP=2 call %PICTBAT%sh2shPolar ^ %SRC% s2s_cent_mask.png . . s2s_pol3.png``` With more supersampling: ```set s2spSUP_SAMP=4 call %PICTBAT%sh2shPolar ^ %SRC% s2s_cent_mask.png . . s2s_pol4.png set s2spSUP_SAMP=``` With supersampling 2, no mask: ```set s2spSUP_SAMP=2 call %PICTBAT%sh2shPolar ^ %SRC% s2s_cent_mask.png . . s2s_pol5.png 0 set s2spSUP_SAMP=``` Reverse transformation of the source image: ```call %PICTBAT%sh2shPolar ^ %SRC% s2s_cent_mask.png . r s2s_pol6.png 0``` Reverse transformation of the forward transformation: ```set s2spSUP_SAMP= call %PICTBAT%sh2shPolar ^ s2s_pol5.png s2s_cent_mask.png . r s2s_pol7.png 0 set s2spSUP_SAMP=``` (This is clearly wrong.)

Supersampling factor 2 seems to work well. Processing time is proportional to the square of the supersampling factor.

The mask used above have the property that each white line (horizontal, vertical or radial, depending on the method) is contiguous. We can break this condition. When the mask is not contiguous, source pixels are spread across that part of the mask.

 s2s_cent_mask2.png: Horizontal linear, so we get horizontal spreading of pixels: ```call %PICTBAT%sh2shLinear ^ %SRC% s2s_cent_mask2.png . . . s2s_lin_nc.png 0``` Polar, so we get radial spreading of pixels: ```call %PICTBAT%sh2shPolar ^ %SRC% s2s_cent_mask2.png . . s2s_pol_nc.png 0```

## Mathematical shapes with the scripts

Mathematical shapes can be used with these scripts. Examples below show the mask, a horizontal linear transformation, and a polar transformation.

 Rectangle: ```set s2spSUP_SAMP=2 %IMG7%magick ^ %SRC% ^ -fill #000 -colorize 100 ^ -fill #fff ^ -draw "rectangle 100,75 300,225" ^ s2s_ms_m1.png call %PICTBAT%sh2shLinear ^ %SRC% s2s_ms_m1.png . . . s2s_ms_l1.png 0 call %PICTBAT%sh2shPolar ^ %SRC% s2s_ms_m1.png . . s2s_ms_p1.png 0``` Off-centre rectangle: ```%IMG7%magick ^ %SRC% ^ -fill #000 -colorize 100 ^ -fill #fff ^ -draw "rectangle 50,25 250,175" ^ s2s_ms_m2.png call %PICTBAT%sh2shLinear ^ %SRC% s2s_ms_m2.png . . . s2s_ms_l2.png 0 call %PICTBAT%sh2shPolar ^ %SRC% s2s_ms_m2.png . . s2s_ms_p2.png 0``` Circle: ```%IMG7%magick ^ %SRC% ^ -fill #000 -colorize 100 ^ -fill #fff ^ -draw "circle 200,150 200,0" ^ s2s_ms_m3.png call %PICTBAT%sh2shLinear ^ %SRC% s2s_ms_m3.png . . . s2s_ms_l3.png 0 call %PICTBAT%sh2shPolar ^ %SRC% s2s_ms_m3.png . . s2s_ms_p3.png 0``` Off-center circle: ```%IMG7%magick ^ %SRC% ^ -fill #000 -colorize 100 ^ -fill #fff ^ -draw "circle 100,100 100,0" ^ s2s_ms_m4.png call %PICTBAT%sh2shLinear ^ %SRC% s2s_ms_m4.png . . . s2s_ms_l4.png 0 call %PICTBAT%sh2shPolar ^ %SRC% s2s_ms_m4.png . . s2s_ms_p4.png 0 set s2spSUP_SAMP=```

## Future

Techniques shown here might be used to morph between shapes, possibly with animation.

## Scripts

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

### grid.bat

```rem Makes a grid with specified number of intervals.
rem %1,%2 are width and height.
rem %3 and %4 are number of gaps between horizontal and vertical grid lines, >=1 (default 4)
rem %5 is whether to draw diagonal (default 0)
rem %6 is grid colour (default white)
rem %7 is background colour, can be "none" (default black)
rem %8 is colour for boundary
rem %9 is output file (default grid.png)
@rem
@rem Also uses:
@rem   gridSTROKE_WIDTH default 1
@rem
@rem
@rem Updated:
@rem   5-August-2022 for IM v7.
@rem

rem No required arguments.
rem @if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

set WW=%1
if "%WW%"=="." set WW=
if "%WW%"=="" set WW=150
set HH=%2
if "%HH%"=="." set HH=
if "%HH%"=="" set HH=100

set nX=%3
if "%nX%"=="." set nX=
if "%nX%"=="" set nX=4
set nY=%4
if "%nY%"=="." set nY=
if "%nY%"=="" set nY=4

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

set GRID_COL=%6
if "%GRID_COL%"=="." set GRID_COL=
if "%GRID_COL%"=="" set GRID_COL=White

set BACK_COL=%7
if "%BACK_COL%"=="." set BACK_COL=
if "%BACK_COL%"=="" set BACK_COL=Black

set BOUND_COL=%8
if "%BOUND_COL%"=="." set BOUND_COL=

set OUTFILE=%9
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=grid.png

if "%gridSTROKE_WIDTH%"=="" set gridSTROKE_WIDTH=0

FOR /F "usebackq" %%L ^
IN (`%IMG7%magick identify ^
-format ^
Wm1^=%%[fx:%WW%-1]\n^
Hm1^=%%[fx:%HH%-1]\n^
dX^=%%[fx:%WW%/%nX%]\n^
dY^=%%[fx:%HH%/%nY%]\n^
nXm1^=%%[fx:%nX%-1]\n^
nYm1^=%%[fx:%nY%-1] ^
xc:`) ^
DO set %%L

set LINESFILE=%TEMP%\gridlines.txt
del %LINESFILE% 2>nul

if %nXm1% GTR 0 for /L %%i in (1, 1, %nXm1%) do (
FOR /F %%L IN ('%IMG7%magick identify ^
-format "v=%%[fx:int(%dX%*%%i+0.5)]" xc:') DO @set %%L
echo line !v!,0 !v!,%Hm1% >>%LINESFILE%
)

if %nYm1% GTR 0 for /L %%i in (1, 1, %nYm1%) do (
FOR /F %%L IN ('%IMG7%magick identify ^
-format "v=%%[fx:int(%dY%*%%i+0.5)]" xc:') DO @set %%L
echo line 0,!v! %Wm1%,!v! >>%LINESFILE%
)

if %DO_DIAG%==1 (
echo line %Wm1%,0 0,%Hm1% >>%LINESFILE%
echo line 0,0 %Wm1%,%Hm1% >>%LINESFILE%
)

if not "%BOUND_COL%"=="" (
echo fill %BOUND_COL% >>%LINESFILE%
echo line 0,0 %Wm1%,0 >>%LINESFILE%
echo line 0,0 0,%Hm1% >>%LINESFILE%
echo line %Wm1%,0 %Wm1%,%Hm1% >>%LINESFILE%
echo line 0,%Hm1% %Wm1%,%Hm1% >>%LINESFILE%
)

if %gridSTROKE_WIDTH%==1 (
set sSTROKEW=
) else (
set sSTROKEW=-strokewidth %gridSTROKE_WIDTH%
)

%IMG7%magick ^
-size %WW%x%HH% ^
xc:%BACK_COL% ^
-fill None ^
-stroke %GRID_COL% ^
%sSTROKEW% ^
-draw "@%LINESFILE%" ^
%OUTFILE%

call echoRestore```

### sh2shLinear.bat

```@rem Given image %1
@rem and mask %2, white shape on black background,
@rem distorts image into white shape with horizontal or linear displacement.
@rem
@rem Optional arguments:
@rem   %3 direction of displacement
@rem        h=horizontal, v=vertical [h]
@rem   %4 proportion towards displacement (0 to 100) [100]
@rem   %5 reverse transformation (shape -> rectangle) NYI
@rem        f=forwards, r=reverse [f]
@rem   %6 output filename [*_s2sl.*]
@rem        y=yes, n=no [y]
@rem   %8 filename for output relative displacement map [no file]
@rem
@rem Updated:
@rem   27-August-2022 for IM v7.
@rem

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 s2sl

set DIRN=%3
if "%DIRN%"=="." set DIRN=
if "%DIRN%"=="" set DIRN=h

set PROP_DIST=%4
if "%PROP_DIST%"=="." set PROP_DIST=
if "%PROP_DIST%"=="" set PROP_DIST=100

set FOR_REV=%5
if "%FOR_REV%"=="." set FOR_REV=
if "%FOR_REV%"=="" set FOR_REV=f

set OUTF=%6
if "%OUTF%"=="." set OUTF=
if "%OUTF%"=="" set OUTF=%OUTFILE%
set OUTFILE=%OUTF%

set OUT_MAP=%8
if "%OUT_MAP%"=="." set OUT_MAP=
if "%OUT_MAP%"=="" set OUT_MAP=

for /F "usebackq tokens=1-6 delims=x+-," %%A ^
in (`%IMG7%magick ^
-bordercolor Black -border 1 ^
-format "%%@,%%w,%%h" ^
info:`) ^
do (
set /A MTW=%%A
set /A MTH=%%B
set /A MTX=%%C-1
set /A MTY=%%D-1
set /A MWW=%%E-2
set /A MHH=%%F-2
)
echo Mask: MTW=%MTW% MTH=%MTH% MTX=%MTX% MTY=%MTY% MWW=%MWW% MHH=%MHH%

set sSHRINK=
if /I "%DIRN%"=="h" if not %MTH%==%MHH% (
echo Shrink height

set sSHRINK=-resize "%TW%x%TH%^^^!" ^
-background Blue ^
-extent %WW%x%HH%-%TX%-%TY%
)

if /I "%DIRN%"=="v" if not %MTW%==%MWW% (
echo Shrink width

set sSHRINK=-resize "%TW%x%TH%^^^!" ^
-background Blue ^
-extent %WW%x%HH%-%TX%-%TY%
)

if /I "%DIRN%"=="h" (
set sSPARSE=-sparse-color Bilinear ^
"0,0 Black %%[fx:w-1],0 White"
set ROTR=
set ROTF=

set sDISP=-set option:compose:args %MWW%x0
)

if /I "%DIRN%"=="v" (
set sSPARSE=-sparse-color Bilinear ^
0,0,#000,0,%%[fx:h-1],#fff
set ROTR=-rotate -90
set ROTF=-rotate 90

set sDISP=-set option:compose:args 0x%Hm1%
)

if "%PROP_DIST%"=="100" (
set sPROP=
) else (
for /F "usebackq" %%L in (`%IMG7%magick identify ^
-format="%%[fx:100-%PROP_DIST%]" ^
xc:`) do set sPROP=-fill gray^(50%%^) -colorize %%L

set sMASK=^^^( +clone -fill #fff -colorize 100 ^
-virtual-pixel Black mpr:DISP ^
-compose Displace ^
%sDISP% -composite -write s.png ^^^)
)

echo sPROP=%sPROP%
echo sDISP=%sDISP%

if /I "%FOR_REV%"=="r" (
set sINVERSE=-process invclut
) else (
set sINVERSE=
)

) else (
)

%IM7DEV%magick ^
%INFILE% ^
%sSHRINK% ^
+depth ^
( -clone 1 ^
%ROTR% ^
-process 'cumulhisto norm' ^
%sINVERSE% ^
%ROTF% ^
( +clone ^
%sSPARSE% ^
) ^
-compose Mathematics ^
-define compose:args=0,-0.5,0.5,0.5 ^
-composite ^
%sPROP% ^
-write mpr:DISP ^
+write sh2sh_map.png ^
) ^
-delete 1 ^
-compose Displace ^
%sDISP% -composite ^
%OUTFILE%

call echoRestore```

### sh2shPolar.bat

```@rem Given image %1
@rem and mask %2, white shape on black background,
@rem distorts image into white shape with horizontal or polar displacement.
@rem
@rem Optional arguments:
@rem   %3 proportion towards displacement (0 to 100) [100]
@rem   %4 reverse transformation (shape -> rectangle)
@rem        f=forwards, r=reverse (r is buggy; FIXME) [f]
@rem   %5 output filename [*_s2sp.*]
@rem        1=yes, 0=no [1]
@rem   %7 filename for output relative displacement map [no file] (NYI)
@rem
@rem Also uses:
@rem   s2spSUP_SAMP Factor for supersampling [default 1].
@rem
@rem Updated:
@rem   22-August-2022 Upgraded for IM v7.
@rem
@rem FIXME: also need method of specifying central coord.
@rem        (If neither specified, leave blank in "-distort".)
@rem

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 s2sp

set PROP_DIST=%3
if "%PROP_DIST%"=="." set PROP_DIST=
if "%PROP_DIST%"=="" set PROP_DIST=100

set FOR_REV=%4
if "%FOR_REV%"=="." set FOR_REV=
if "%FOR_REV%"=="" set FOR_REV=f

set OUTF=%5
if "%OUTF%"=="." set OUTF=
if "%OUTF%"=="" set OUTF=%OUTFILE%
set OUTFILE=%OUTF%

set OUT_MAP=%7
if "%OUT_MAP%"=="." set OUT_MAP=
if "%OUT_MAP%"=="" set OUT_MAP=

set SUP_SAMP=%s2spSUP_SAMP%
if "%SUP_SAMP%"=="" set SUP_SAMP=1

for /F "usebackq" %%L in (`%IMG7%magick identify ^
-precision 15 ^
-format "Wm1=%%[fx:%SUP_SAMP%*w-1]\nHm1=%%[fx:%SUP_SAMP%*h-1]\nSUP_SAMPinvPc=%%[fx:100/%SUP_SAMP%]\nSUP_SAMPinv=%%[fx:1/%SUP_SAMP%]" ^
%INFILE%`) do set %%L

set sUNROLL=-virtual-pixel Background ^
-distort depolar -1

set sROLL=-distort polar -1

set sCOMPMATH=-compose Mathematics ^
-define compose:args=0,-0.5,0.5,0.5 ^
-composite

if "%PROP_DIST%"=="100" (
set sPROP=
) else (
for /F "usebackq" %%L in (`%IMG7%magick identify ^
-format="%%[fx:100-%PROP_DIST%]" ^
xc:`) do set sPROP=-fill gray^(50%%^) -colorize %%L
)

if /I "%FOR_REV%"=="r" (
set sREVERSE=-rotate -90 -process invclut -rotate 90

set sREVERSE=-rotate -90 ^
+write z0.png ^
mpr:IDENT ^
-compose Mathematics -define compose:args=0,1,1,-0.5 -composite ^
+write z1.png ^
-process invclut ^
mpr:IDENT ^
-compose Mathematics -define compose:args=0,-1,1,0.5 -composite ^
+write z2.png ^
-rotate 90

) else (
set sREVERSE=
)

) else (
)

%IM7DEV%magick ^
-define distort:scale=%SUP_SAMP% ^
%INFILE% ^
%sUNROLL% ^
+depth ^
( -clone 1 ^
( -clone 0 ^
-fill White -colorize 100 ^
-background Black ^
%sUNROLL% ^
-rotate -90 ^
-process 'cumulhisto norm' ^
-process 'invclut' ^
( +clone ^
-sparse-color Bilinear ^
"0,0 Black %%[fx:w-1],0 White" ^
-write mpr:IDENT ^
) ^
%sCOMPMATH% ^
+write x0.png ^
mpr:IDENT ^
+swap ^
-compose Displace ^
-set option:compose:args %Hm1%x0 -composite ^
) ^
( -clone 0 ^
+write x1b.png ^
-background Black ^
%sUNROLL% ^
-rotate -90 ^
-process 'cumulhisto norm' ^
+write x1a.png ^
mpr:IDENT ^
%sCOMPMATH% ^
+write x1.png ^
) ^
-delete 0 ^
-write info: ^
-compose Displace ^
-set option:compose:args %Hm1%x0 -composite ^
mpr:IDENT ^
%sCOMPMATH% ^
-rotate 90 ^
%sPROP% ^
%sREVERSE% ^
+write x2.png ^
) ^
-delete 1 ^
-compose Displace ^
-set option:compose:args 0x%Hm1% -composite ^
-define distort:scale=%SUP_SAMPinv% ^
-virtual-pixel None ^
-distort polar -1 ^
-depth 16 ^
%OUTFILE%

rem IM bug? if %OUTFILE% is .png, we get corrupt file, unless we have "-depth 16".

call echoRestore

@endlocal & set sh2shOUTFILE=%OUTFILE%```

### stretchHoriz.bat

```rem Given %1 is an image,
rem %2 is a mask, mostly white, with black at left and right edges,
rem makes %3 that has image stretched to fill the bounding rectangle of the white pixels.
rem %4 direction: h=horizontally, v=vertically. Default h.
rem %5 supersample percentage eg 400. Default 100 (no supersampling).
@rem
@rem Updated:
@rem   27-August-2022 for IM v7.
@rem

setlocal

set INFILE=%1
set OUTFILE=%3

set TMP_IN=sh_tmp.miff

set DIRN=%4
if "%DIRN%"=="." set DIRN=
if "%DIRN%"=="" set DIRN=h

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

if /I %DIRN%==h (
set ROT_START=
set ROT_END=
) else if /I %DIRN%==v (
set ROT_START=-rotate -90
set ROT_END=-rotate 90
) else (
echo %0: Invalid direction [%DIRN%]
exit /B 1
)

for /F "usebackq tokens=1-4 delims=x+" %%A in (`%IMG7%magick ^
-bordercolor Black -border 1 ^
-format "%%@" ^
info:`) do (
set MW=%%A
set MH=%%B
set /A MX=%%C-1
set /A MY=%%D-1
)

if %RESAMP_PC%==100 (
set RESAMP_START=
set RESAMP_END=
) else (
set RESAMP_START=-resize %RESAMP_PC%%%
set RESAMP_END=-resize "%MW%x%MH%^!"
)

set UNUSED_COL=Blue

set WW=
for /F "usebackq" %%L in (`%IMG7%magick ^
%INFILE% ^
%ROT_START% ^
%RESAMP_START% ^
-format "WW=%%w\nHH=%%h" ^
+write info: ^
+depth ^
%%TMP_IN%%`) do set %%L
if "%WW%"=="" exit /B 1

set EDGE_COL=blue

%IMG7%magick ^
+depth ^
%ROT_START% ^
%RESAMP_START% ^
-threshold 50%% ^
+repage ^
( -clone 0 ^
-sparse-color bilinear ^
0,0,#000,^
%%[fx:w-1],0,#f00,^
0,%%[fx:h-1],#0f0,^
%%[fx:w-1],%%[fx:h-1],#ff0 ^
) ^
( -clone 0 ^
-fill %EDGE_COL% -colorize 100 ^
) ^
-swap 0,2 ^
-compose Over -composite ^
-crop x1 +repage ^
-bordercolor %EDGE_COL% -border 1 ^
-trim +repage ^
-resize "%WW%x1^!" ^
-append ^
%TMP_IN% ^
+swap ^
-compose Distort ^
-set option:compose:args 100%%x100%% -composite ^
%ROT_END% ^
%RESAMP_END% ^
%OUTFILE%

if ERRORLEVEL 1 exit /B 1

endlocal```

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

My usual version of IM is:

`%IMG7%magick -version`
```Version: ImageMagick 7.1.1-15 Q16-HDRI x64 a0a5f3d:20230730 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
Features: Cipher DPC HDRI OpenCL OpenMP(2.0)
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 (193532217)```

This customised development version is:

`%IM7DEV%magick -version`
```Version: ImageMagick 7.1.1-5 (Beta) Q32-HDRI x86_64 852a723f1:20230319 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
Features: Cipher DPC HDRI Modules OpenMP(4.5)
Delegates (built-in): bzlib cairo fftw fontconfig freetype heic jbig jng jpeg lcms ltdl lzma pangocairo png raqm raw rsvg tiff webp wmf x xml zip zlib
Compiler: gcc (11.3)```

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

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.