snibgo's ImageMagick pages

Coffee mug

How can we superimpose an image on to a coffee mug? If the mug can be modelled in POV-Ray, there are two obvious methods.

  1. Ray-trace a mug that incorporates the desired image as a pigment.
  2. Ray-trace a white mug. Use ImageMagick to superimpose an image.

This page demonstrates both methods.

The version of IM used must be at least Q16. Q8 should work but results will be disappointing as this permits only 256 values for displacement in each direction.

The methods shown on this page can be applied with any 3D modelling software, on any object that can be modelled. The Painting people page shows the same methods applied with Blender and ImageMagick.

Sample image

We create an image that we will wrap around a mug.

%IMG7%magick ^
  ( -size 1200x400 gradient:#88f-#8f8 ) ^
  ( -size 1200x400 ^
    -background None ^
    -fill Black label:snibgo ^
    -fill White -page -10-10 label:snibgo ^
    -layers merge +repage ^
    -trim +repage -resize "1200x400^!" ) ^
  -composite ^
  +write cm_samp_srgb.png ^
  -colorspace RGB ^
  cm_samp.png

This is the sRGB version, cm_samp_srgb.png:

cm_samp_srgb.png

The RGB version is to incorporate as an element in a scene for POV-Ray to render.

Ray-traced mug and image together

The POV-Ray library has a suitable mug, CoffeeMug, "version: Cousin Ricky 2.0". That webpage has a downloadable zip that contains three files we will use:

Those files are: Copyright 2008 - 2015 Richard Callwood III. Some rights reserved. Licensed under the terms of the CC-LGPL. See http://creativecommons.org/licenses/LGPL/2.1/ for more information.

As delivered, coffeemug.pov takes a square image, and wraps two copies of that image around the mug: one on the front, the other on the back. It also rounds the corners of the image. (With default arguments, it also draws copies of the POV-Ray "P" logo.)

coffeemug_map.jpg

This image, supplied in the zip file, is encoded as sRGB.

coffeemug_map.jpg

Default rendering of coffeemug.pov:

POV-Ray assumes the image is encoded as RGB, so the result is too light.

%POV% ^
  -D +A0.01 +W600 +H450 +FN16 +UA ^
  +Icoffeemug.pov +Ocm_coffeemug.png
cm_coffeemug.pngjpg

For convenience, I use environment variables for POV-Ray:

set POV 
POV=c:\ProgramFiles\POV-Ray\v3.7\bin\pvengine64
POVDIR=c:\ProgramFiles\POV-Ray\v3.7\bin\
POVUSER=C:\Users\Alan\documents\POV-Ray\v3.7\

I wanted one wide image that would wrap around most of the mug. That image could be built in ImageMagick, either as a wide crop from a photograph, or two images side-by-side, or whatever I wanted. So I copied coffeemug.pov to a new file cm_coffeemug.inc which I hacked about, removing stuff I didn't want, adding other stuff.

When editing scripts, remember that POV-Ray numbers represent transparency but ImageMagick numbers represent opacity. In POV-Ray, zero is opaque. In ImageMagick, zero is transparent.

For the mug modelled here, and the real mug I use daily, an image of aspect ratio 3:1 fits neatly.

Then I created a few simple scene files named cm_*.pov that use cm_coffeemug.inc. They also use cm_trans.inc, which creates whatever transformation I want to the mugs in all the scenes. By putting the transformation in an included file, I only need to update it once if I want all mugs at a different angle.

Default rendering of cm_scene.pov:

We append three copies of the square image to make the aspect ratio 3:1.

We encode it as RGB, as POV-RAY requires.

%IMG7%magick ^
  coffeemug_map.jpg ^
  -duplicate 2 +append +repage ^
  -colorspace RGB ^
  cm_3.png

%POV% ^
  -D +A0.01 +W600 +H450 +FN16 +UA ^
  +Icm_scene.pov +Ocm_coffeemug2.png
cm_coffeemug2.pngjpg

No page on ray-tracing is complete without:

%POV% ^
  -D +A0.01 +W600 +H450 +FN16 +UA ^
  +Icm_mug_trans.pov +Ocm_mug_trans.png
cm_mug_trans.pngjpg

The image that is to be wrapped around the mug must have certain characteristics:

With POV-Ray option +Q0, the only lighting is a fixed ambient, with no shading or shadows or reflections or transparency, so we get the exact colours that were specified for the mug and image. With a black mug and floor, and white image, we get a mask for just the image. With a black floor, white mug and image, we get a mask for just the mug. With a linear gradient for the image (horizontal or vertical, or using the red and green channels), we get an absolute displacement map that is directly usable by ImageMagick.

From the POV-Ray command line, we can assign floating-point values to identifiers that are used in a .pov script. We can't pass string values, or vectors or any other type in the command line.

The best way to pass string argument seems to be:

  1. Echo a line like #declare Image_File = "cm_i_white.png" to a .inc file.
  2. In the POV-Ray script, #include this .inc file. If this is unconditional but the file doesn't exist, POV-Ray will fail. So I include it only if cm_extra_inc is defined as 1.
  3. The name of the .inc file is hard-coded. I chose coffeemug_extra.inc.

Ray-tracing the mug with our "snibgo" image wrapped around it:

echo #declare Image_File = "cm_samp.png" ^
  >coffeemug_extra.inc

call StopWatch

%POV% ^
  -D +A0.01 +W600 +H450 +FN16 +UA ^
  Declare=cm_extra_inc=1 ^
  Declare=FocalBlur=1 ^
  +Icm_scene.pov +Ocm_snibgo_mug_pov.png

call StopWatch 
0 00:01:40

Focal blur makes this very slow. Without focal blur, it only takes a couple of seconds.

The foreground floor chequer pattern is reflected in the mug.

cm_snibgo_mug_pov.pngjpg

Ray-traced mug with superimposed image

Suppose we have a web server, and want to offer a facility where users can upload an image, and we create a picture of a mug with the user's image wrapped around it. Suppose the web server has ImageMagick loaded, but not POV-Ray.

The solution is simple, in concept. Offline, we make an identity absolute displacement map, and wrap that around the mug with POV-Ray. This gives us the displacement map that IM can use on the server to wrap any image of the same aspect ratio around that mug.

An identity absolute displacement map has a horizontal gradient increasing left-to-right in the red channel, and a vertical gradient increasing top-to-bottom in the green channel.

We start by making some ray-tracings. These are done once only, on any computer that can run POV-Ray. We make:

The absolute displacement map must be high precision: at least 16 bits/channel/pixel, possibly 32-bit floating point. If this image must be edited, such as to remove items that should be wrapped, be careful that the colours don't change. Any slight colour change invisible to the human eye will cause a displacement change that is highly visible. Photo editors are often "colour-managed", which means they mess around changing colorspace and dot-gain, so the pixel values change. But this "image" isn't really an image, and such processing will damage the data.

Using ImageMagick, we take the ray-traced mug and make a lighting model.

For each image that a user uploads, we can displace it, implement the lighting model, and compose it over the mug using just ImageMagick.

Render cm_scene.pov with white image:

%IMG7%magick -size 3x1 xc:White cm_i_white.png

echo #declare Image_File = "cm_i_white.png" ^
  >coffeemug_extra.inc

%POV% ^
  -D +A0.01 +W600 +H450 +FN16 +UA ^
  Declare=cm_extra_inc=1 ^
  Declare=FocalBlur=1 ^
  +Icm_scene.pov +Ocm_white_mug.png

The foreground floor chequer pattern is reflected in the mug.

cm_white_mug.pngjpg

White mug on no floor, so the background is transparent:

%POV% ^
  -D +A0.01 +W600 +H450 +FN16 +UA ^
  Declare=FocalBlur=1 ^
  +Icm_mug_plain.pov +Ocm_plain_mug.png

Highlights from the handle are apparent in the body of the mug.

cm_plain_mug.png

Mug mask:

%POV% ^
  -D +A0.01 +W600 +H450 +FN16 +UA ^
  +Q0 ^
  +Icm_mug_mask.pov +Ocm_mug_mask.png
cm_mug_mask.png

Image mask (we use a white image on a black mug):

%IMG7%magick -size 3x1 xc:White cm_i_white.png

echo #declare Image_File = "cm_i_white.png" ^
  >coffeemug_extra.inc

%POV% ^
  -D +A0.01 +W600 +H450 +FN16 +UA ^
  Declare=MugColR=0 ^
  Declare=MugColG=0 ^
  Declare=MugColB=0 ^
  Declare=cm_extra_inc=1 ^
  +Q0 ^
  +Icm_image_mask.pov +Ocm_image_mask.png
cm_image_mask.png

Create an identity displacement map,
and wrap it around the mug:

%IMG7%magick ^
  cm_samp.png ^
  -sparse-color Bilinear ^
0,0,#008,^
%%[fx:w-1],0,#f08,^
0,%%[fx:h-1],#0f8,^
%%[fx:w-1],%%[fx:h-1],#ff8 ^
  -set colorspace sRGB ^
  -colorspace RGB ^
  +depth ^
  cm_i_disp.png

echo #declare Image_File = "cm_i_disp.png" ^
  >coffeemug_extra.inc

%POV% ^
  -D +A0.01 +W600 +H450 +FN16 +UA ^
  Declare=MugColR=0 ^
  Declare=MugColG=0 ^
  Declare=MugColB=0 ^
  Declare=cm_extra_inc=1 ^
  +Q0 ^
  +Icm_map_mask.pov +Ocm_disp_map.png
cm_disp_map.png

The displacement maps must be at least 16 bits/channel/pixel. If they were only 8 b/c/p, they would have only 256 different horizontal displacements and we need at least 1200, so we would get blocky results.

%IMG7%magick identify cm_i_disp.png 
%IMG7%magick identify cm_disp_map.png 
cm_i_disp.png PNG 1200x400 1200x400+0+0 16-bit RGB 6596B 0.000u 0:00.001
cm_disp_map.png PNG 600x450 600x450+0+0 16-bit sRGB 98432B 0.016u 0:00.001

16 bits, integer, is sufficient for web-size images. Larger images need more precision, eg 7500x5000 pixels need Q32 HDRI.

In the displacement map, POV-Ray has antialiased between the colour and black. This will give an incorrect distortion at the edge of the image. We need to extend the colour portion slightly into the black area. I will increase the size by 5% on each side, so 10% in each dimension, using distort:viewport. The POV-Ray script is independent of the image size, so we also need to tell the script to make the image 10% larger on the mug, using a POV-Ray script variable I have called Image_Mult.

A small problem remains at the edge of the map, against the transparent background. This is solved by compositing the map over a slight blur of itself.

Extended displacement map:

%IMG7%magick ^
  cm_i_disp.png ^
  -virtual-pixel Edge ^
  -define distort:viewport=1320x440-60-20 ^
  -distort SRT 1,0 +repage ^
  cm_i_extdisp.png

echo #declare Image_File = "cm_i_extdisp.png" ^
  >coffeemug_extra.inc

%POV% ^
  -D +A0.001 +AM2 +R5 +W600 +H450 +FN16 +UA ^
  Declare=MugColR=0 ^
  Declare=MugColG=0 ^
  Declare=MugColB=0 ^
  Declare=cm_extra_inc=1 ^
  Declare=Image_Mult=1.1 ^
  +Q0 ^
  +Icm_map_mask.pov +Ocm_extdisp_map.png

%IMG7%magick ^
  cm_extdisp_map.png ^
  +repage ^
  ( +clone -blur 0x1 -alpha off ) ^
  -compose DstOver -composite ^
  -background Black -alpha Background -alpha Off ^
  cm_extdisp_map.png
cm_extdisp_map.png

This has expanded the distortion map so it partly wraps around the handle, and we can clearly see the start of the map (or the right side of the image). This excess will be removed by the mask.

+UA tells POV-Ray to write transparency. This makes the colour carry to the left and right edges of the distorted area, instead of anti-aliasing. We then remove transparency with IM. If, instead, we had ray-traced a black floor, we would have had a darkened edge to the map, so the displacement would be wrong.

Check the precision of the extended map:

%IMG7%magick identify cm_extdisp_map.png 
cm_extdisp_map.png PNG 600x450 600x450+0+0 16-bit sRGB 139218B 0.000u 0:00.001

Distorting the image by the map is slightly messy, because we want the image to be at least as tall as the map, and at least as wide. The script wrapImage.bat calculates a resizing percentage for the image.

The script uses ImageMagick, not POV-Ray. It takes a conventional rectangular image and wraps it around a mug that has been pre-prepared with POV-Ray.

Use IM to wrap the image around the mug:

call StopWatch

call %PICTBAT%wrapImage ^
  cm_samp_srgb.png ^
  cm_white_mug.png ^
  cm_extdisp_map.png ^
  cm_image_mask.png ^
  2 ^
  cm_snibgo_mug_im.png

call StopWatch 
0 00:00:05
cm_snibgo_mug_im.pngjpg

How different is this from the version entirely created by POV-Ray?

%IMG7%magick compare -metric RMSE cm_snibgo_mug_pov.png cm_snibgo_mug_im.png NULL: 

cmd /c exit /B 0
1212.85 (0.0185068)

The difference is entirely within the distorted image, of course. Comparing crops of the main area:

%IMG7%magick ^
  cm_snibgo_mug_pov.png ^
  -crop 250x300+65+102 +repage ^
  cm_snibgo_mug_pov_crop.png
cm_snibgo_mug_pov_crop.png
%IMG7%magick ^
  cm_snibgo_mug_im.png ^
  -crop 250x300+65+102 +repage ^
  cm_snibgo_mug_im_crop.png
cm_snibgo_mug_im_crop.png

The geometry and colours are correct. The main differences are:

Specular highlights

When light hits a shiny surface, some is immediately reflected without taking the colour of surface. When this hits the camera, we see a patch the same colour as the light (in this case, white) even when the surface has a strong colour.

We ray-trace a red mug. Most light that reaches the camera has been diffusely reflected from the mug, which has absorbed green and blue wavelengths, so only red light hits the camera.

Where the normal of the surface is mid-way between the ray arriving from the light source and the ray leaving towards the camera, we have a specular highlight. The surface reflects the light more like a mirror, without absorbing wavelengths, so it remains white.

%POV% ^
  -D +A0.01 +W600 +H450 +FN16 +UA ^
  Declare=MugColR=1 ^
  Declare=MugColG=0 ^
  Declare=MugColB=0 ^
  Declare=FocalBlur=1 ^
  +Icm_mug_plain.pov +Ocm_spec_mug.png
cm_spec_mug.png

Make a mask from the specular highlights:

%IMG7%magick ^
  cm_spec_mug.png ^
  -channel G -separate ^
  cm_spec_mask.png
cm_spec_mask.png

Highlights are visible on the rim and handle. Light has hit the lower part of the handle, reflected in a specular way towards the body of the mug, and reflected from there to the camera.

In this example, no specular highlights are reflected by the graphic image on the mug, so we will not process this further. The script wrapImageH.bat can be used when highlights are to be added.

Compositing over photographs

To create a realistic composition, say of a large coffee mug in a road, we need to model the real world in POV-Ray. Here, I make the simple assumption that the road is a smooth horizontal plane.

The mug casts a shadow on the ground. To get a shadow that is softer when it is further away from the mug, we use #declare Soft=on in the POV-Ray script to create an area light. The command line could also have Declare=FocalBlur=1, but this heavily increases processing time and makes hardly any difference.

Create the shadow of the mug on the floor.

%POV% ^
  -D +A0.01 +W600 +H450 +FN16 +UA ^
  +Icm_mug_shad.pov +Ocm_mug_shad.png

%IMG7%magick ^
  cm_mug_shad.png ^
  cm_mug_mask.png ^
  -compose Lighten -composite ^
  cm_mug_shad.png
cm_mug_shad.png

To use this as a multiplier in a composite,
we want the floor that is not in shadow to be entirely white.

The darkest floor pixel that is not in shadow is the NorthWest corner.

for /F "usebackq" %%V in (`%IMG7%magick ^
  cm_mug_shad.png ^
  -gravity NorthWest -crop 1x1+0+0 ^
  -format "%%[fx:mean*100]" ^
  info:`) do set FLOOR_GRAY=%%V

%IMG7%magick ^
  cm_mug_shad.png ^
  -level 0,%FLOOR_GRAY%%% ^
  cm_mug_shad_wh.png
cm_mug_shad_wh.png

Use IM to wrap the image around the mug, with no floor:

call %PICTBAT%wrapImage ^
  cm_samp_srgb.png ^
  cm_plain_mug.png ^
  cm_extdisp_map.png ^
  cm_image_mask.png ^
  2 ^
  cm_snibgo_mug_im_nf.png
cm_snibgo_mug_im_nf.png

Composite a photo, the mug and its shadow.

set PHOTO=%PICTLIB%20150618\AGA_2352.JPG

%IMG7%magick ^
  %PHOTO% ^
  -resize 600x450^^ ^
  -crop 600x450+0+0 +repage ^
  cm_snibgo_mug_im_nf.png ^
  -compose Over -composite ^
  cm_mug_shad_wh.png ^
  -compose Multiply -composite ^
  +depth ^
  cm_mug_road.png
cm_mug_road.pngjpg

For a better match between the images,
we sharpen the photo (with -unsharp) and blur the mug.

We also blur the shadow slightly more than the mug,
and shift the position of the mug and shadow.

set POSN_MUG=+5+15

set GEOM_MUG=-geometry %POSN_MUG%
set REPAGE_MUG=-repage %POSN_MUG%

%IMG7%magick ^
  %PHOTO% ^
  -resize 600x450^^ ^
  -crop 600x450+0+0 +repage ^
  -unsharp 0x0.5 ^
  ( cm_snibgo_mug_im_nf.png -blur 0x0.5 ) ^
  %GEOM_MUG% ^
  -compose Over -composite ^
  ( cm_mug_shad_wh.png -blur 0x1 ) ^
  %GEOM_MUG% ^
  -compose Multiply -composite ^
  +depth ^
  cm_mug_road2.png
cm_mug_road2.pngjpg

The base looks somewhat "pasted-on". We want to blur and darken this edge.

To do this, we first make a mask of this edge from cm_mug_mask.png. There are many ways to do this. Here, we subtract the image from a copy shifted down by one pixel. This gives us the bottom edge, and not the top edge. We smear vertically with -motion-blur and reduce isolated pixels at the mug edge with median.

We modulate the effect, reducing the effect towards the top, by multiplying by a gradient.

Make a shadow and mask for the base.

%IMG7%magick ^
  cm_mug_mask.png ^
  ( +clone ) ^
  -geometry +0+1 ^
  -compose MinusDst -composite ^
  -motion-blur 0x1+90 ^
  -motion-blur 0x1+270 ^
  -auto-level ^
  -statistic median 3x3 ^
  ( +clone ^
    -sparse-color bilinear ^
      "0,0,#000 0,%%[fx:h-1],#fff" ^
    -sigmoidal-contrast 5,50%% ^
  ) ^
  -compose Multiply -composite ^
  -auto-level ^
  %REPAGE_MUG% ^
  -background Black -compose Over -flatten ^
  cm_base.png
cm_base.png

Blur and darken the base.

%IMG7%magick ^
  cm_mug_road2.png ^
  ( -clone 0 -blur 0x3 ) ^
  ( cm_base.png ) ^
  -alpha off ^
  -compose Over -composite ^
  ( cm_base.png -negate +level 25%%,100%% ) ^
  -compose Multiply -composite ^
  cm_mug_road3.png
cm_mug_road3.pngjpg

The result is a plausible illusion. The base, where the mug is in contact wth the road, isn't quite right. Otherwise, the mug boundary looks good. Although the sharpness appears balanced, a close examination shows the background photo has been sharpened (the white road markings have bright edges) whereas the mug hasn't. If the photo had been resized with a method shown in Resampling with halo minimization, the photo sharpening would be less obvious.

Scripts

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

wrapImage.bat

rem Wraps (distorts, warps) an image onto another image.
rem
rem %1 Image to be wrapped.
rem %2 Image to be overlaid.
rem %3 Absolute distortion map.
rem %4 Mask of image after distortion.
rem %5 Optional supersampling factor, eg 2. [Default = 1 = no supersampling.]
rem %6 Optional output file.
@rem
@rem Updated:
@rem   5-August-2022 for IM v7.
@rem


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

@setlocal

@call echoOffSave

rem call %PICTBAT%setInOut %1 wi

set IMAGE=%1

set MUG=%2

set MAP=%3

set MASK=%4

set nSUPERSAMP=%5
if "%nSUPERSAMP%"=="." set nSUPERSAMP=
if "%nSUPERSAMP%"=="" set nSUPERSAMP=1

if %nSUPERSAMP% GEQ 10 (
  echo nSUPERSAMP is suspiciously large: %nSUPERSAMP%
  exit /B 1
)

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


for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 16 ^
  -format "SUPERSAMPpc=%%[fx:%nSUPERSAMP%*100]" ^
  xc:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "WI=%%w\nHI=%%h" ^
  %IMAGE%`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "WM=%%w\nHM=%%h" ^
  %MAP%`) do set %%L

rem We want WI >= WM and HI >= HM.
rem If this isn't true, we will resize the image.
rem Even if it is true, we might resize it for super-sampling.

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 16 ^
  -format "resPc=%%[fx:FW=%WM%/%WI%;FH=%HM%/%HI%;MF=FW>FH?FW:FH;MF>1?MF*100:100]" ^
  xc:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 16 ^
  -format "resPc=%%[fx:%resPc%*%SUPERSAMPpc%/100]" ^
  xc:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 16 ^
  -format "DX=%%[fx:w*%resPc%/200]\nDY=%%[fx:h*%resPc%/200]" ^
  %IMAGE%`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 16 ^
  -format "invSUPERSAMPpc=%%[fx:10000/%SUPERSAMPpc%]" ^
  xc:`) do set %%L

if %SUPERSAMPpc%==100 (
  set sSUP=
  set sINVSUP=
) else (
  set sSUP=-resize %SUPERSAMPpc%%%
  set sINVSUP=-resize %invSUPERSAMPpc%%%
)

if %resPc%==100 (
  set sRES=
) else (
  set sRES=-resize %resPc%%%
)

:: Following works best with HDRI.
::
%IMG7%magick ^
  ( %IMAGE% %sRES% ) ^
  ( %MAP% %sSUP% ) ^
  -gravity Center ^
  -virtual-pixel Edge ^
  -compose Distort -define compose:args=%DX%x%DY% -composite ^
  %sINVSUP% ^
  -crop %WM%x%HM%+0+0 +repage ^
  -compose Over -extent %WM%x%HM% ^
  %MASK% ^
  -alpha off ^
  -compose CopyOpacity -composite ^
  %MUG% ^
  -compose Multiply -composite ^
  ( +clone -blur 0x1 ) ^
  -compose DstOver -composite ^
  -depth 32 ^
  -compress Zip ^
  %OUTFILE%

rem Alternatives: Multiply, LinearBurn, ColorBurn

if ERRORLEVEL 1 exit /B 1

call echoRestore

endlocal & set wiOUTFILE=%OUTFILE%

An expanded version of the previous, that also adds highlights:

wrapImageH.bat

rem Wraps (distorts, warps) an image onto another image.
rem
rem %1 Image to be wrapped.
rem %2 Image to be overlaid.
rem %3 Absolute distortion map.
rem %4 Mask of image after distortion.
rem %5 Highlight (and shadows) mask.
rem %6 Optional supersampling factor, eg 2. [Default = 1 = no supersampling.]
rem %7 Optional output file.
@rem
@rem This is like wrapImage, but with highlight mask.
@rem This is mid-gray for no-change, up to white.
@rem It is applied with overlay, so darkness in the mask darkens the image.
@rem
@rem
@rem Updated:
@rem   5-August-2022 for IM v7.
@rem


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

@setlocal

@call echoOffSave

rem call %PICTBAT%setInOut %1 wi

set IMAGE=%1

set MUG=%2

set MAP=%3

set MASK=%4

set HIGHLIGHT=%5
if "%HIGHLIGHT%"=="." set HIGHLIGHT=
if "%HIGHLIGHT%"=="" set HIGHLIGHT=

set nSUPERSAMP=%6
if "%nSUPERSAMP%"=="." set nSUPERSAMP=
if "%nSUPERSAMP%"=="" set nSUPERSAMP=1

if %nSUPERSAMP% GEQ 10 (
  echo nSUPERSAMP is suspiciously large: %nSUPERSAMP%
  exit /B 1
)

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


for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 16 ^
  -format "SUPERSAMPpc=%%[fx:%nSUPERSAMP%*100]" ^
  xc:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "WI=%%w\nHI=%%h" ^
  %IMAGE%`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "WM=%%w\nHM=%%h" ^
  %MAP%`) do set %%L

rem We want WI >= WM and HI >= HM.
rem If this isn't true, we will resize the image.
rem Even if it is true, we might resize it for super-sampling.

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 16 ^
  -format "resPc=%%[fx:FW=%WM%/%WI%;FH=%HM%/%HI%;MF=FW>FH?FW:FH;MF>1?MF*100:100]" ^
  xc:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 16 ^
  -format "resPc=%%[fx:%resPc%*%SUPERSAMPpc%/100]" ^
  xc:`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 16 ^
  -format "DX=%%[fx:w*%resPc%/200]\nDY=%%[fx:h*%resPc%/200]" ^
  %IMAGE%`) do set %%L

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -precision 16 ^
  -format "invSUPERSAMPpc=%%[fx:10000/%SUPERSAMPpc%]" ^
  xc:`) do set %%L

if %SUPERSAMPpc%==100 (
  set sSUP=
  set sINVSUP=
) else (
  set sSUP=-resize %SUPERSAMPpc%%%
  set sINVSUP=-resize %invSUPERSAMPpc%%%
)

if %resPc%==100 (
  set sRES=
) else (
  set sRES=-resize %resPc%%%
)

if "%HIGHLIGHT%"=="" (
  set sHILI=
) else (
  set sHILI=%HIGHLIGHT% +swap -compose Overlay -composite
)

echo DX=%DX%  DY=%DY%

%IM7DEV%magick ^
  ( %IMAGE% %sRES% ) ^
  ( %MAP% %sSUP% ) ^
  -gravity Center ^
  -virtual-pixel Edge ^
  -compose Distort -define compose:args=%DX%x%DY% -composite ^
  %sINVSUP% ^
  -crop %WM%x%HM%+0+0 +repage ^
  -compose Over -extent %WM%x%HM% ^
  %MASK% ^
  -alpha off ^
  -compose CopyOpacity -composite ^
  %MUG% ^
  -compose Multiply -composite ^
  ( +clone -blur 0x1 ) ^
  -compose DstOver -composite ^
  %sHILI% ^
  -depth 32 ^
  -compress Zip ^
  %OUTFILE%

rem Alternatives: Multiply, LinearBurn, ColorBurn

if ERRORLEVEL 1 exit /B 1

call echoRestore

endlocal & set wiOUTFILE=%OUTFILE%

POV-Ray files

cm_scene.pov

#version 3.5;

/*
 * #declare Ph=on;
 * #declare Rad=on;
 * #declare Soft=on;
*/

#declare Soft=on;

#include "cm_coffeemug.inc"

#include "cm_trans.inc"

#declare Ph=on;

union {
  object { Chq_Floor }
  object { Coffee_Mug transform MugTrans }
}

cm_mug_plain.pov

#version 3.5;

#include "cm_coffeemug.inc"

#include "cm_trans.inc"

union {
  object { Coffee_Mug_Plain transform MugTrans }
}

cm_mug_trans.pov

#version 3.5;

#declare Ph=on;

#include "cm_coffeemug.inc"

#include "cm_trans.inc"

union {
  object { Chq_Floor }
  object { Coffee_Mug_Trans transform MugTrans }
  object { Bev_Water }
}

cm_trans.inc

This is called by all the coffee mug POV files, and creates whatever transformation I want for the mug.

#declare MugTrans = transform { rotate -50 * y }

cm_map_mask.pov

#version 3.5;

#include "cm_coffeemug.inc"

#include "cm_trans.inc"

object { MugImage transform MugTrans }

cm_mug_mask.pov

#version 3.5;

#include "cm_coffeemug.inc"

#include "cm_trans.inc"

union {
  object { Black_Floor }
  object { Coffee_Mug_Plain transform MugTrans }
}

cm_image_mask.pov

#version 3.5;

#include "cm_coffeemug.inc"

#include "cm_trans.inc"

union {
  object { Black_Floor }
  object { MugImage transform MugTrans }
}

cm_mug_shad.pov

#version 3.5;

#declare Soft=on;

#include "cm_coffeemug.inc"

#include "cm_trans.inc"

union {
  object { White_Floor }
  object { Coffee_Mug_Plain transform MugTrans }
}

cm_coffeemug.inc

/* cm_coffeemug.inc
 * 2015 Alan Gibson (snibgo applied arts).
 *
 * Derived from:
 *   coffeemug.pov version 2.0 by Richard Callwood III.
 *
 */

#include "strings.inc"
#include "colors.inc"
#include "woods.inc"
#include "shapes.inc"
#include "glass.inc"
#include "coffeemug.inc"

#ifndef (cm_extra_inc) #declare cm_extra_inc = 0; #end

#if (cm_extra_inc = 1)
#include "coffeemug_extra.inc"
#end

#ifndef (Ph) #declare Ph = off; #end // photons
#ifndef (Rad) #declare Rad = off; #end // radiosity
#ifndef (Soft) #declare Soft = off; #end // soft shadows

#ifndef (Texture) #declare Texture = 1; #end

#ifndef (FocalBlur) #declare FocalBlur = 0; #end

#declare C_SKY = rgb 0.01;

#ifndef (MugColR) #declare MugColR = 1; #end
#ifndef (MugColG) #declare MugColG = 1; #end
#ifndef (MugColB) #declare MugColB = 1; #end

#declare MugCol = rgb <MugColR, MugColG, MugColB>;

#default // for wood texture only
{ finish
  { reflection { 0 0.2 fresnel } conserve_energy
    ambient (Rad? 0: C_SKY)
  }
}

#ifndef (MugRad) #declare MugRad = 1.5; #end
#ifndef (MugHt) #declare MugHt = 3.75; #end

#ifndef (Image_File) #declare Image_File = "cm_3.png"; #end
#ifndef (Image_Mult) #declare Image_Mult = 1; #end

//=============================== ENVIRONMENT ==================================

global_settings
{ assumed_gamma 1
  #if (Rad)
    radiosity
    { count 100
      error_bound 0.5
      pretrace_start 0.08
      pretrace_end 0.01
      recursion_limit 2
    }
  #end
  #if (Ph) photons { spacing 0.005 autostop 0 } #end
  max_trace_level 10
}

camera
// { location <1, 13.5, -18>
{ location <2, 10, -18>
  look_at <1, MugHt/2, 0>
  #if (FocalBlur=1)
    aperture 0.7
    blur_samples 20
    focal_point <0, MugHt/2, -MugRad>
  #end
  angle 20
}

#declare LIGHT_POSITION = <-0.5, 1, -1> * 60;
light_source
{ LIGHT_POSITION, rgb 1.2
  fade_power 2 fade_distance vlength (LIGHT_POSITION)
  spotlight point_at <0, MugHt/2, 0> radius 5 falloff 10
  #if (Soft) area_light 2.5 * x, 2.5 * z, 9, 9 adaptive 1 circular orient #end
}

//----- A little ambience -----
background { C_SKY }

//----- Estimate ambient light -----
#declare Illumination = vdot (vnormalize (LIGHT_POSITION), y);
#declare c_Table = rgb <0.295, 0.163, 0.118>; // sampled from T_Wood12
#declare LIT = 0.8; // estimate of contribution of spotlit portion of the table
#declare DIFFUSE = 0.6; // this scene's diffuse (which is the POV-Ray default)
#declare Ambience =
( Rad?
  0:
  (C_SKY + c_Table * (C_SKY + Illumination * DIFFUSE * LIT)) / 2
);
#default { finish { ambient Ambience } }

//----- The table -----
#declare Floor =
plane {
  y, 0
  texture {
    T_Wood12
    rotate <0, 5, 0>
    scale 2
  }
  interior { ior 1.47 }
}

#declare Chq_Floor =
plane {
  y, 0
  pigment {
    checker color Black, color White
  }
}

#declare White_Floor =
plane {
  y, 0
  pigment { rgb 1 }
}

#declare Black_Floor =
plane {
  y, 0
  pigment { rgb 0 }
}

//=============================== MUG TEXTURES =================================

//------------------- Texture elements ---------------------

#declare Scale_Image = 1.4 * Image_Mult;

#declare p_Image = pigment
{// Note: Image_File should be gamma=1.
  image_map { png Image_File once interpolate 2 }
  translate -0.5 // must be centered on the origin
  scale Scale_Image * <6, 2, 1>
}

#declare o_Pic_area = box {-Scale_Image * <3, 1, 1>, Scale_Image * <3, 1, 1>}

#declare f_Gloss = finish
{ specular 4.24 roughness 0.002
  reflection { 0.05 0.4 fresnel } conserve_energy
}

#declare m_Glass = material
{
  texture {
    pigment { Col_Glass_Green }
    finish { F_Glass5 }
  }
  interior { I_Glass1 }
}


//-------------------- Final textures ----------------------

#declare p_Design = CoffeeMug_Pigment
( MugRad, 0, MugHt, MUG_HEIGHT, o_Pic_area, MugCol, p_Image
)


//============================= COFFEE MATERIAL ================================

#declare m_Coffee = material
{ texture
  { pigment { rgbf <0.9, 0.7, 0.4, 1> }
    finish
    { specular 0.64 roughness 0.01
      reflection { 0, 1 fresnel } conserve_energy
    }
  }
  interior
  { ior 1.34
    media
    { absorption <0.25, 0.4, 0.75>
      scattering { 3, rgb 1 }
    }
  }
}

#declare m_Water = material
{ texture
  { pigment { rgbt <0.9, 0.9, 0.9, 1> }
    finish
    { specular 0.64 roughness 0.01
      reflection { 0, 1 fresnel } conserve_energy
    }
  }
  interior {
    ior 1.333
    fade_distance 5
    fade_power 1001.0
    fade_color <0.02, 0.20, 0.06>
  }
}

//================================ SHOWPIECE ===================================

#declare Coffee_Mug =
object
{ CoffeeMug_Mug (MugRad, MugHt, MUG_HEIGHT, no)
  pigment { p_Design }
  finish { f_Gloss }
  interior { ior 1.6 }
  #if (Ph) photons { target reflection on } #end
}

#declare Coffee_Mug_Trans =
object
{ CoffeeMug_Mug (MugRad, MugHt, MUG_HEIGHT, yes)
  pigment { rgbt <1, 0.5, 0.5, 1> }
  finish { f_Gloss }
  interior { ior 1.6 }
  #if (Ph) photons { target reflection on } #end
}

#declare Coffee_Mug_Plain =
object
{ CoffeeMug_Mug (MugRad, MugHt, MUG_HEIGHT, yes)
  pigment { rgb <MugColR, MugColG, MugColB> }
  finish { f_Gloss }
}

#declare Bev_Coffee =
object {
  CoffeeMug_Beverage (MugRad, 0, 8, MUG_INCH, 0.001)
  hollow
  material { m_Coffee }
}

#declare Bev_Water =
object {
  CoffeeMug_Beverage (MugRad, 0, 8, MUG_INCH, 0.001)
  hollow
  material { m_Water }
  #if (Ph) photons { target refraction on } #end
}

#declare MugImage =
object
{ CoffeeMug_Mug (MugRad, MugHt, MUG_HEIGHT, yes)
  CoffeeMug_Pigment
  ( MugRad, 0, MugHt, MUG_HEIGHT, o_Pic_area, rgbt <MugColR, MugColG, MugColB,1>, p_Image
  )
  finish { f_Gloss }
}

// end of coffeemug.pov

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

%IMG7%magick -version
Version: ImageMagick 7.1.0-42 Q16-HDRI x64 396d87c:20220709 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenCL 
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 (193231332)

... and POV-Ray 3.7, with "Render, On completion, Exit POV-Ray for Windows" ticked.

"POV-Ray" is a trademark of Persistence of Vision Raytracer Pty Ltd.

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


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 7-June-2015.

Page created 07-Aug-2022 03:39:08.

Copyright © 2022 Alan Gibson.