We can distort a shape to match another shape.
The general problem is to distort an arbitrary shape into another arbitrary shape.
Building blocks for this page are:
Some commands and scripts on this page use those process modules. Without those modules, the commands won't work.
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% |
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
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,
%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 |
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.
s2s_cent_mask.png |
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,
%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. |
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 %7 mask-down (make transparent where mask is black) @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,
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. |
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.
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.
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 |
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 %6 mask-down (make transparent where mask is black) @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 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= |
Techniques shown here might be used to morph between shapes, possibly with animation.
For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.
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
@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 %7 mask-down (make transparent where mask is black) @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 MASK_FILE=%2 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 MASK_DOWN=%7 if "%MASK_DOWN%"=="." set MASK_DOWN= if "%MASK_DOWN%"=="" set MASK_DOWN=y 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 ^ %MASK_FILE% ^ -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= set sMASK=mpr:MASK ) 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% echo sMASK=%sMASK% if /I "%FOR_REV%"=="r" ( set sINVERSE=-process invclut ) else ( set sINVERSE= ) if /I "%MASK_DOWN%"=="y" ( set sMASK_DOWN=%sMASK% -alpha off -compose CopyOpacity -composite ) else ( set sMASK_DOWN= ) %IM7DEV%magick ^ %INFILE% ^ %sSHRINK% ^ %MASK_FILE% ^ +depth ^ ( -clone 1 ^ -write mpr:MASK ^ %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 ^ %sMASK_DOWN% ^ %OUTFILE% call echoRestore
@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 %6 mask-down (make transparent where mask is black) @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 MASK_FILE=%2 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 MASK_DOWN=%6 if "%MASK_DOWN%"=="." set MASK_DOWN= if "%MASK_DOWN%"=="" set MASK_DOWN=1 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= ) if "%MASK_DOWN%"=="1" ( set sMASK_DOWN=mpr:MASK -alpha off -compose CopyOpacity -composite ) else ( set sMASK_DOWN= ) %IM7DEV%magick ^ -define distort:scale=%SUP_SAMP% ^ %INFILE% ^ %sUNROLL% ^ ( %MASK_FILE% -write mpr:MASK ) ^ +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 ^ %sMASK_DOWN% ^ -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%
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 MASK=%2 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 ^ %MASK% ^ -bordercolor Black -border 1 ^ -format "%%@" ^ info:`) do ( set MW=%%A set MH=%%B set /A MX=%%C-1 set /A MY=%%D-1 ) set MASK_CROP=-crop %MW%x%MH%+%MX%+%MY% +repage 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% ^ %MASK_CROP% ^ %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 ^ %MASK% ^ +depth ^ %MASK_CROP% ^ %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
All images on this page were created by the commands shown.
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 License: https://imagemagick.org/script/license.php 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 License: https://imagemagick.org/script/license.php 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.
Anyone is permitted to link to this page, including for commercial use.
Page version v1.0 8-October-2014.
Page created 20-Sep-2023 11:21:09.
Copyright © 2023 Alan Gibson.