snibgo's ImageMagick pages

Curving in 3D

We distort the image plane, like curving a piece of paper.

Curving an image in three-dimensional space is easily performed by ray-tracers such as POV-ray. The ray-tracer can create displacement maps and lighting masks for known surfaces so ImageMagick can apply them to any arbitrary input image.

However, some curved surfaces can be directly modelled with just ImageMagick. The method show here is not geometrically accurate for perspective views, but is good enough for some purposes.

The goal

Given an input image and a line that represents the cross-section of a surface, make a 2-D image that resembles the input curved into the surface.

Imagine the input image is printed on a sheet of paper. We can curve this paper in a number of ways, and even fold it. However, we will not cut the paper, nor will we stretch it.

In addition, we will curve the input image along the x-dimension only. Straight horizontal lines will become curved, but vertical lines will remain straight.

Sample inputs

We will distort this input image:

set SRC=c3d_src.png

call %PICTBAT%gridOver ^
  toes.png %SRC% 8 8
c3d_src.pngjpg

Images don't usually have grids, but this helps us see what is happening.

First, we need the profile of the curve we want to make. This is a cross-section of the 3-D curve. For a cylinder, it would be a circle. However, it must have two ends.

I use Gimp to draw a thin white line on a black background. The line can start and end anywhere, but it must not cross itself.

c3d_line.png

c3d_line.png

The size of the image that contains the line is of no significance. The length of the line is significant, and so is the width it occupies within the image. We find the bounding rectangle of the line with the script colTrim.bat. An alternative method is to use "-connected-components".

call %PICTBAT%colTrim c3d_line.png Black

echo ctRECT=%ctRECT%  ctW=%ctW%  ctH=%ctH%
ctRECT=239x137+22+49  ctW=239  ctH=137

The script dp2Grad.bat finds the two ends of the line, and the length of the line in pixels. It also creates a gradient along the line that is black at one end, white at the other. Pixels that are not on the line are made transparent. (The morphology operation, and hence the entire script, is horribly slow.) We trim the result to just the line.

call %PICTBAT%dp2grad ^
  c3d_line.png ^
  c3d_grad.png

%IM%convert ^
  c3d_grad.png ^
  -crop %ctRECT% +repage ^
  c3d_grad.png

echo l2gLINE_LEN=%l2gLINE_LEN% l2gERR=%l2gERR% 
l2gLINE_LEN= l2gERR= 
c3d_grad.png

dp2grad writes some properties to the file.

%IM%identify -format %%[line:*] c3d_grad.png 
line:len=368
line:x0=22
line:x1=260
line:y0=169
line:y1=185

We can read these to environment variables. In Windows, variable names can't contain colons (:), so we change them to underscores.

for /F "usebackq" %%L in (`%IM%identify ^
  -format %%[line:*] ^
  c3d_grad.png`) do (
  set LINE=%%L
  set !LINE::=_!
)

set line_ 
line_len=368
line_x0=22
line_x1=260
line_y0=169
line_y1=185

The length of the line needs to be equal to the width of the input image. Currently, the line is too long. We could rectify this by shrinking the line. (For precision, we might then re-calculate the length, and repeat until the length is sufficiently close.) Instead, we will enlarge the input image. We also need the new dimensions.

for /F "usebackq" %%L in (`%IM%convert ^
  %SRC% ^
  -resize %line_len%x ^
  -format "INP_WW=%%w\nINP_HH=%%h" ^
  +write info: ^
  c3d_res_inp.png`) do set %%L

echo INP_WW=%INP_WW% INP_HH=%INP_HH% 
INP_WW=368 INP_HH=321 
c3d_res_inp.pngjpg

We fill the transparent areas by filling vertically. (See Filling holes: fill by shift.)

rem set sfFLAT_COL=None

call %PICTBAT%shiftFill ^
  c3d_grad.png 0x c3d_fill.png

rem set sfFLAT_COL=
c3d_fill.png

If the line hasn't doubled back on itself, the rows will be equal. The top row represents a horizontal absolute displacement map, to transform an input image to the curved surface.

We crop the top row and scale it to the required height (INP_HH). It is the correct scale horizontally, but needs to be extended to the right to match the input.

%IM%convert ^
  c3d_fill.png ^
  -crop 0x1+0+0 +repage ^
  -scale "x%INP_HH%^!" ^
  -gravity West ^
  -background White ^
  -extent %INP_WW%x%INP_HH% ^
  c3d_xdisp.png
c3d_xdisp.pngjpg

X-displacement

From c3d_xdisp.png, we make a complete absolute displacement map.

Make the map.

%IM%convert ^
  c3d_xdisp.png ^
  ( -clone 0 ^
    -sparse-color bilinear ^
      "0,0 Black 0,%%[fx:h-1] White" ^
  ) ^
  ( -clone 0 ^
    -fill gray(50%%) -colorize 100 ^
  ) ^
  -combine ^
  -set colorspace sRGB ^
  c3d_x_abs.png
c3d_x_abs.pngjpg

Apply the map, and crop.

%IM%convert ^
  c3d_res_inp.png ^
  c3d_x_abs.png ^
  -compose Distort ^
  -set option:compose:args 100%%x100%% ^
  -composite ^
  -crop %ctW%x+0+0 +repage ^
  c3d_xdisp_trial.png
c3d_xdisp_trial.pngjpg

If we took a photo from an infinite distance of our input image wrapped onto the surface, looking directly at the surface, this is what the resulting photo would look like.

Y-displacement

Instead of looking directly at the surface, we might view it from an angle. This is equivalent to rotating the surface around the x-axis by an angle α. This has two effects:

  1. The y-axis is fore-shortened, by a factor of cos(α).
  2. We will see the profile of the surface, as defined by the line image. This is also fore-shortened, by a factor of sin(α).

The angle is between -90° and +90°. If the angle is zero, we have no y-displacement. At either extreme, we see the exact profile, with none of the input image.

The total height of the output will be the sum of the foreshortened height of the input, plus the foreshortened height of the profile. This is INP_HH * cos(α) + ctH * |sin(α)| where INP_HH and ctH are given above.

set ALPHA=30

set ALPH_RAD=(%ALPHA%*3.1415926/180)

for /F "useback" %%L in (`%IM%identify ^
  -format "inHcosA=%%[fx:%INP_HH% * cos(%ALPH_RAD%)]\nctHsinA=%%[fx:%ctH% * sin(%ALPH_RAD%)]"
  xc:`) do set %%L

for /F "useback" %%L in (`%IM%identify ^
  -format "OUT_HH=%%[fx:%inHcosA% + %ctHsinA%]\nctHsinA_2=%%[fx:%ctHsinA%/2]"
  xc:`) do set %%L

echo inHcosA=%inHcosA% ctHsinA=%ctHsinA% ctHsinA_2=%ctHsinA_2% OUT_HH=%OUT_HH% 
inHcosA=277.994 ctHsinA=68.5 ctHsinA_2=34.25 OUT_HH=346.494 

We can get a height field from the cross section by trimming to the line, turning the pixels beneath it white, and scaling to a single row. This will be used for any y-displacement.

%IM%convert ^
  c3d_line.png ^
  -crop %ctRECT% +repage ^
  -morphology dilate rectangle:1x%ctH%+0+0 ^
  -scale "x1^!" ^
  c3d_ht_fld.png
c3d_ht_fld.png

From c3d_ht_fld.png, we can make a relative displacement map.

Make the map.

%IM%convert ^
  c3d_ht_fld.png ^
  ( -clone 0 ^
    -fill gray(50%%) -colorize 100 ^
  ) ^
  ( -clone 1 ) ^
  -swap 0,1 ^
  -combine ^
  -set colorspace sRGB ^
  -scale "x%OUT_HH%^!" ^
  c3d_y_rel.png
c3d_y_rel.pngjpg

Apply the map.

%IM%convert ^
  c3d_xdisp_trial.png ^
  -scale "x%inHcosA%^!" ^
  -gravity Center -background None ^
  -extent "x%OUT_HH%^!" ^
  c3d_y_rel.png ^
  -virtual-pixel None ^
  -compose Displace ^
  -set option:compose:args 0x%ctHsinA_2% ^
  -composite ^
  c3d_ydisp_trial.png
c3d_ydisp_trial.pngjpg

This is like taking a photo of the curved image from an infinite distance, with no perspective ("parallel projection").

Y-displacement with perspective

We can simulate perspective by:

  1. reducing the height of the profile at the top of the image;
  2. reducing the distance between the horizontal parallels at the top of the image;
  3. narrowing the width at the top of the image.

For effect (1) and (2), we "+level" the map, to push values towards 50%. We apply this to a clone of the entire map, and blend this with the original map by composing "over" with a mask.

Revise the map.

%IM%convert ^
  c3d_y_rel.png ^
  ( +clone ^
    +level 10%% ^
  ) ^
  ( +clone ^
    -sparse-color bilinear "0,0,White 0,%%[fx:h-1],Black" ^
  ) ^
  -composite ^
  c3d_y_persp.png
c3d_y_persp.pngjpg

We use this revised map, and apply the narrowing process.

Apply perspective.

set NARROW_PC=10

set dNarrow=(w*%NARROW_PC%/100)

set PERSP=^
0,0,%%[fx:%dNarrow%],0,^
%%[fx:w-1],0,%%[fx:w-1-%dNarrow%],0,^
0,%%[fx:h-1],0,%%[fx:h-1],^
%%[fx:w-1],%%[fx:h-1],%%[fx:w-1],%%[fx:h-1]

%IM%convert ^
  c3d_xdisp_trial.png ^
  -scale "x%inHcosA%^!" ^
  -gravity Center -background None ^
  -extent "x%OUT_HH%^!" ^
  c3d_y_persp.png ^
  -virtual-pixel None ^
  -compose Displace ^
  -set option:compose:args 0x%ctHsinA_2% ^
  -composite ^
  -virtual-pixel None ^
  -distort Perspective "%PERSP%" ^
  c3d_ydisp_persp.png
c3d_ydisp_persp.pngjpg

Because the profile height has reduced at the top (the back of the 3-D object), the overall result is shorter than OUT_HH.

The shortening of the profile height is not quite accurate (it varies at each cross-section), but visually appears realistic. Similarly, for correct 3-D, the distance between parallels on the ridge should be greater than the distance in the valleys, but this has reduced the distance. For ordinary photographs without grids, these defects should not be noticable.

The script

We encapsulate the above in the script curve3d.bat.

Make
line file
Line file Run script Output

c3d_line.png

c3d_line.png
call %PICTBAT%curve3d ^
  %SRC% c3d_line.png ^
  c3d_s1.png
c3d_s1.pngjpg

c3d_line.png

c3d_line.png
call %PICTBAT%curve3d ^
  %SRC% c3d_line.png ^
  c3d_s2.png ^
  20 20 20
c3d_s2.pngjpg

c3d_line.png

c3d_line.png
call %PICTBAT%curve3d ^
  %SRC% c3d_line.png ^
  c3d_s3.png ^
  -20 20 20
c3d_s3.pngjpg
%IM%convert ^
  -size 300x200 ^
  xc:Black ^
  +antialias ^
  -stroke White -fill None ^
  -draw ^
    "arc 20,190 280,20 180,0" ^
  c3d_arc.png
c3d_arc.png
call %PICTBAT%curve3d ^
  %SRC% c3d_arc.png ^
  c3d_s4.png ^
  -20 20 20
c3d_s4.pngjpg
%IM%convert ^
  -size 300x200 ^
  xc:Black ^
  +antialias ^
  -stroke White -fill None ^
  -draw ^
    "ellipse 150,100 130,80 0,360" ^
  -stroke Black ^
  -draw "line 150,100 150,199" ^
  c3d_ellip.png

The black line breaks the circle
to make the two ends for the line.

The output is twice the size of the arc example,
but the back half is hidden.

c3d_ellip.png
call %PICTBAT%curve3d ^
  %SRC% c3d_ellip.png ^
  c3d_s5.png ^
  20 20 20
c3d_s5.pngjpg

Future

The methods shown here could be extended:

Scripts

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

colTrim.bat

rem Find trim rectangle for border of given colour.

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

@setlocal

@call echoOffSave

call %PICTBAT%setInOut %1 ct

set BORD_COL=%2
if "%BORD_COL%"=="." set BORD_COL=
if "%BORD_COL%"=="" set BORD_COL=Black

if not "%3"=="" set OUTFILE=%3


for /F "usebackq tokens=1-4 delims=x+" %%A in (`%IM%convert ^
  %INFILE% ^
  -bordercolor %BORD_COL% ^
  -border 1 ^
  -format "%%@" ^
  info:`) do (
  set /A BW=%%A
  set /A BH=%%B
  set /A BX=%%C-1
  set /A BY=%%D-1
)


call echoRestore

endlocal & set ctW=%BW%& set ctH=%BH%& set ctX=%BX%& set ctY=%BY%& set ctRECT=%BW%x%BH%+%BX%+%BY%

dp2Grad.bat

rem Given image %1 a white line on black background,
rem finds coords of the ends,
rem and makes output image %2 that has gradient for the line,
rem and transparent elsewhere.
rem Sets properties "line:*" in output.

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

@setlocal

rem @call echoOffSave

call %PICTBAT%setInOut %1 dp2g

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


set TMP_2ENDS=l2g_two_ends.miff

set ERR=0

set X0=
for /F "usebackq tokens=2,3 delims=:, " %%X in (`%IMDEV%convert ^
  %INFILE% ^
  -virtual-pixel Black ^
  -morphology HMT LineEnds ^
  -process onewhite ^
  %TMP_2ENDS% 2^>^&1`) do (
  set X0=%%X
  set Y0=%%Y
)
if "%X0%"=="" exit /B 1

if "%X0%"=="none" (
  echo %0: no line ends [%INFILE%]
  set ERR=1
  goto end
)

for /F "usebackq tokens=2,3 delims=:, " %%X in (`%IMDEV%convert ^
  %TMP_2ENDS% ^
  -fill Black -draw "point %X0%,%Y0%" ^
  -process onewhite ^
  NULL: 2^>^&1`) do (
  set X1=%%X
  set Y1=%%Y
)

if "%X1%"=="none" (
  echo %0: no second line end [%INFILE%]
  set ERR=1
  goto end
)

set nInPath=
for /F "usebackq tokens=1-2 delims=: " %%L in (`%IMDEV%convert ^
  %INFILE% ^
  -negate ^
  -evaluate Subtract 1 -clamp -evaluate Add 1 ^
  -process ^
    "darkestpntpnt s %X0%,%Y0% e %X1%,%Y1% t 0.5 data v" ^
  -channel G -separate +channel ^
  -clamp ^
  -auto-level ^
  +depth ^
  %OUTFILE% 2^>^&1`) do (
  if /I "%%L"=="nInPath" set nInPath=%%M
  if /I "%%L"=="maxDist" set MaxDist=%%M
  if /I "%%L"=="Bug:" set ERR=1
)
if "%nInPath%"=="" exit /B 1

%IMDEV%convert ^
  %OUTFILE% ^
  %INFILE% ^
  -compose CopyOpacity -composite ^
  -background gray(50%%) -alpha Background ^
  -auto-level ^
  -alpha Background ^
  -set line:len %nInPath% ^
  -set line:x0 %X0% ^
  -set line:y0 %Y0% ^
  -set line:x1 %X1% ^
  -set line:y1 %Y1% ^
  %OUTFILE%

:end

call echoRestore

endlocal & set dp2gOUTFILE=%OUTFILE%& set dp2gLINE_LEN=%LINE_LEN%&set dp2gERR=%ERR%

line2Grad.bat

The methods shown on this page originally used line2Grad.bat. Now, dp2Grad.bat does the same job but is much quicker.

rem Given image %1 a white line on black background,
rem finds coords of the ends,
rem and makes output image %2 that has gradient for the line,
rem and transparent elsewhere.
rem Sets properties "line:*" in output.


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

@setlocal

@call echoOffSave

call %PICTBAT%setInOut %1 l2g

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


set TMP_2ENDS=l2g_two_ends.miff
set TMP_LINE_GRAD=l2g_grad.png

set APPROX_LEN_PC=90

set ERR=0


set WW=

for /F "usebackq" %%L in (`%IM%identify ^
  -format "WW=%%w\nHH=%%h\nDIAG=%%[fx:int(hypot(w,h)+0.5)]" ^
  %INFILE%`) do set %%L

if "%WW%"=="" (
  echo Can't open [%INFILE%]
  exit /B 1
)


for /F "usebackq tokens=2,3 delims=:, " %%X in (`%IMDEV%convert ^
  %INFILE% ^
  -virtual-pixel Black ^
  -morphology HMT LineEnds ^
  -process onewhite ^
  %TMP_2ENDS% 2^>^&1`) do (
  set X0=%%X
  set Y0=%%Y
)

if "%X0%"=="none" (
  echo %0: no line ends [%INFILE%]
  set ERR=1
  goto end
)

for /F "usebackq tokens=2,3 delims=:, " %%X in (`%IMDEV%convert ^
  %TMP_2ENDS% ^
  fill Black -draw "point %X0%,%Y0%" ^
  -process onewhite ^
  NULL: 2^>^&1`) do (
  set X1=%%X
  set Y1=%%Y
)

if "%X1%"=="none" (
  echo %0: no second line end [%INFILE%]
  set ERR=1
  goto end
)

:: FIXME:
:: If X1 < X0, swap the ends.
:: If X1==X0 and Y1 < Y0, swap the ends.

rem echo X0=%X0% Y0=%Y0% X1=%X1% Y1=%Y1%

for /F "usebackq" %%L in (`%IM%convert ^
  %INFILE% ^
  -format "APPROX_LEN=%%[fx:int(max(hypot(%X1%-%X0%,%Y1%-%Y0%),mean*w*h+0.5))]" ^
  INFO:`) do set %%L

rem echo APPROX_LEN=%APPROX_LEN%

set /A KNL_SCALE_V=65535*%APPROX_LEN_PC%/%APPROX_LEN%/100

rem echo KNL_SCALE_V=%KNL_SCALE_V%

%IM%convert ^
  %INFILE% ^
  -negate +write mpr:MASK ^
  -fill White -colorize 100 ^
  -fill Black -draw "point %X0%,%Y0%" ^
  -mask mpr:MASK ^
  -morphology ^
    IterativeDistance:%DIAG% ^
    Euclidean:7,%KNL_SCALE_V% ^
  +mask ^
  +depth ^
  %TMP_LINE_GRAD%

for /F "usebackq" %%L in (`%IM%convert ^
  %INFILE% ^
  %TMP_LINE_GRAD% ^
  -compose Darken -composite ^
  -format "MAX_01=%%[fx:maxima]\nMAX_GRAY=%%[fx:maxima*QuantumRange]\nLINE_LEN=%%[fx:maxima*QuantumRange/%KNL_SCALE_V%]" ^
  info:`) do set %%L

rem echo MAX_01=%MAX_01% MAX_GRAY=%MAX_GRAY% LINE_LEN=%LINE_LEN%

if "%MAX_01%"=="1" (
  echo %0: Bad first MAX_01 == %MAX_01%
  set ERR=1
  goto end
)

for /F "usebackq" %%L in (`%IM%convert ^
  %INFILE% ^
  %TMP_LINE_GRAD% ^
  -compose Darken -composite ^
  -format "KNL_SCALE_V=%%[fx:%KNL_SCALE_V%/maxima)]" ^
  info:`) do set %%L

rem echo KNL_SCALE_V=%KNL_SCALE_V%

%IM%convert ^
  %INFILE% ^
  -negate +write mpr:MASK ^
  -fill White -colorize 100 ^
  -fill Black -draw "point %X0%,%Y0%" ^
  -mask mpr:MASK ^
  -morphology ^
    IterativeDistance:%DIAG% ^
    Euclidean:7,%KNL_SCALE_V% ^
  +mask ^
  +depth ^
  %TMP_LINE_GRAD%

for /F "usebackq" %%L in (`%IM%convert ^
  %INFILE% ^
  %TMP_LINE_GRAD% ^
  -compose Darken -composite ^
  -format "MAX_01=%%[fx:maxima]\nMAX_GRAY=%%[fx:maxima*QuantumRange]\nLINE_LEN=%%[fx:maxima*QuantumRange/%KNL_SCALE_V%]" ^
  info:`) do set %%L

rem echo MAX_01=%MAX_01% MAX_GRAY=%MAX_GRAY% LINE_LEN=%LINE_LEN%

:: "%MAX_01%" should be "1", more or less.
:: MAX_GRAY should be >= QuantumRange - KNL_SCALE_V

for /F "usebackq" %%L in (`%IM%identify ^
  -format "GRAY_OK=%%[fx:%MAX_GRAY%>QuantumRange-%KNL_SCALE_V%?1:0]"
  xc:`) do set %%L

if not "%GRAY_OK%"=="1" (
  echo %0: Bad second MAX_01 = %MAX_01% MAX_GRAY = %MAX_GRAY%
  set ERR=1
  goto end
)

%IM%convert ^
  %TMP_LINE_GRAD% ^
  %INFILE% ^
  -compose CopyOpacity -composite ^
  -background gray(50%%) -alpha Background ^
  -auto-level ^
  -alpha Background ^
  -set line:len %LINE_LEN% ^
  -set line:x0 %X0% ^
  -set line:y0 %Y0% ^
  -set line:x1 %X1% ^
  -set line:y1 %Y1% ^
  %OUTFILE%

:end

call echoRestore

endlocal & set l2gOUTFILE=%OUTFILE%& set l2gLINE_LEN=%LINE_LEN%&set l2gERR=%ERR%

curve3d.bat

rem From image %1
rem and profile %2 (white line on black background),
rem creates output %3, curving the image around the extruded profile.
rem %4 is tilt of object. >0 tilts up (ie camera tilt down).
rem %5 is percentage perspective narrowing (0=no perspective).
rem %6 is percentage perspective shortening


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

@setlocal enabledelayedexpansion

rem @call echoOffSave

call %PICTBAT%setInOut %1 c3d


set LINE_FILE=%2
set LINE_BASE=%~n2

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

set ALPHA=%4
if "%ALPHA%"=="." set ALPHA=
if "%ALPHA%"=="" set ALPHA=0

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

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

:: FIXME if alpha less than 0, different perspective.

call %PICTBAT%colTrim %LINE_FILE% Black

echo ctRECT=%ctRECT%  ctW=%ctW%  ctH=%ctH%

:: FIXME: grad file shld be naned after the line, not the first input file.

set GRAD_FILE=%LINE_BASE%_c3d_grad.miff
set TMP_INPUT=c3d_tmp_inp.miff
set FILL_FILE=c3d_fill.miff
set XDISP_TRIAL=c3d_xdisp_trial.miff
set Y_PERSP=c3d_y_persp.miff

echo GRAD_FILE=%GRAD_FILE%

set line_len=
if exist %GRAD_FILE% (

  for /F "usebackq" %%L in (`%IM%identify ^
    -format %%[line:*] ^
    %GRAD_FILE%`) do (
    set LINE=%%L
    set !LINE::=_!
  )
)

if "%line_len%"=="" (
  call %PICTBAT%dp2grad ^
    %LINE_FILE% ^
    %GRAD_FILE%

  %IM%convert ^
    %GRAD_FILE% ^
    -crop %ctRECT% +repage ^
    %GRAD_FILE%

  for /F "usebackq" %%L in (`%IM%identify ^
    -format %%[line:*] ^
    %GRAD_FILE%`) do (
    set LINE=%%L
    set !LINE::=_!
  )
)

echo line_len=%line_len% dp2gERR=%dp2gERR%
if "%line_len%"=="" exit /B 1

for /F "usebackq" %%L in (`%IM%convert ^
  %INFILE% ^
  -resize %line_len%x ^
  -format "INP_WW=%%w\nINP_HH=%%h" ^
  +write info: ^
  %TMP_INPUT%`) do set %%L

echo INP_WW=%INP_WW% INP_HH=%INP_HH% 


call %PICTBAT%shiftFill ^
  %GRAD_FILE% 0x %FILL_FILE%

if ERRORLEVEL 1 exit /B 1

%IM%convert ^
  %FILL_FILE% ^
  -crop 0x1+0+0 +repage ^
  -scale "x%INP_HH%^!" ^
  -gravity West ^
  -background White ^
  -extent %INP_WW%x%INP_HH% ^
  ( -clone 0 ^
    -sparse-color bilinear ^
      "0,0 Black 0,%%[fx:h-1] White" ^
  ) ^
  ( -clone 0 ^
    -fill gray(50%%) -colorize 100 ^
  ) ^
  -combine ^
  -set colorspace sRGB ^
  %TMP_INPUT% ^
  +swap ^
  -compose Distort ^
  -set option:compose:args 100%%x100%% ^
  -composite ^
  -crop %ctW%x+0+0 +repage ^
  %XDISP_TRIAL%


set ALPH_RAD=(%ALPHA%*3.1415926/180)

for /F "useback" %%L in (`%IM%identify ^
  -format "inHcosA=%%[fx:%INP_HH% * cos(%ALPH_RAD%)]\nctHsinA=%%[fx:%ctH% * abs(sin(%ALPH_RAD%))]"
  xc:`) do set %%L

for /F "useback" %%L in (`%IM%identify ^
  -format "OUT_HH=%%[fx:%inHcosA% + %ctHsinA%]\nctHsinA_2=%%[fx:%ctHsinA%/2]\nIS_NEG=%%[fx:%ALPHA%^<0?1:0]"
  xc:`) do set %%L

echo inHcosA=%inHcosA% ctHsinA=%ctHsinA% ctHsinA_2=%ctHsinA_2% OUT_HH=%OUT_HH% IS_NEG=%IS_NEG%

set dNarrow=w*%NARROW_PC%/100

if %IS_NEG%==1 (

  echo Angle is negative: %ANGLE%

  set sNEG_Y=-negate

  set PERSP=^
0,0,0,0,^
%%[fx:w-1],0,%%[fx:w-1],0,^
0,%%[fx:h-1],%%[fx:%dNarrow%],%%[fx:h-1],^
%%[fx:w-1],%%[fx:h-1],%%[fx:w-1-%dNarrow%],%%[fx:h-1]

) else (

  echo Angle is positive: %ANGLE%

  set sNEG_Y=

  set PERSP=^
0,0,%%[fx:%dNarrow%],0,^
%%[fx:w-1],0,%%[fx:w-1-%dNarrow%],0,^
0,%%[fx:h-1],0,%%[fx:h-1],^
%%[fx:w-1],%%[fx:h-1],%%[fx:w-1],%%[fx:h-1]

)

%IM%convert ^
  %LINE_FILE% ^
  -crop %ctRECT% +repage ^
  -morphology dilate rectangle:1x%ctH%+0+0 ^
  -scale "x1^!" ^
  ( -clone 0 ^
    -fill gray(50%%) -colorize 100 ^
  ) ^
  ( -clone 1 ) ^
  -swap 0,1 ^
  -combine ^
  -set colorspace sRGB ^
  -scale "x%OUT_HH%^!" ^
  %sNEG_Y% ^
  ( +clone ^
    +level %SHORTEN_PC%%% ^
  ) ^
  ( +clone ^
    -sparse-color bilinear "0,0,White 0,%%[fx:h-1],Black" ^
  ) ^
  -composite ^
  %Y_PERSP%

%IM%convert ^
  %XDISP_TRIAL% ^
  -scale "x%inHcosA%^!" ^
  -gravity Center -background None ^
  -extent "x%OUT_HH%^!" ^
  %Y_PERSP% ^
  -virtual-pixel None ^
  -compose Displace ^
  -set option:compose:args 0x%ctHsinA_2% ^
  -composite ^
  -virtual-pixel None ^
  -distort Perspective "%PERSP%" ^
  %OUTFILE%


call echoRestore

endlocal & set c3dOUTFILE=%OUTFILE%

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

%IM%convert -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 curve3d.h1. To re-create this web page, execute "procH1 curve3d".


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 2-May-2016.

Page created 03-Aug-2016 04:03:47.

Copyright © 2016 Alan Gibson.