A method for filling holes in very sparse displacement map.
Given a very sparse image, gnuplot can guess the unknown values.
Scripts on this page use gnuplot, sed, grep and sort, which are commonly available for Unix and Windows.
For the source, we use an image with a grid so we can see what is happening.
set SRC=dgp_src.png call %PICTBAT%gridOver ^ toes.png %SRC% 10 10 1 yellow %IMG7%magick ^ %SRC% ^ -fill None -stroke White ^ -draw "translate %%[fx:w*0.15],%%[fx:h*0.40] circle 0,0,0,10" ^ -draw "translate %%[fx:w*0.85],%%[fx:h*0.50] circle 0,0,0,10" ^ -draw "translate %%[fx:w*0.50],%%[fx:h*0.85] circle 0,0,0,10" ^ %SRC% for /F "usebackq" %%L in (`%IMG7%magick ^ %SRC% ^ -format "WW=%%w\nHH=%%h\n" ^ info:`) do set %%L echo WW=%WW% HH=%HH% WW=267 HH=233 |
We make a relative displacement map. Values of 50% in the red and green channels will cause zero displacement. Higher values will pull colours from the right or down; lower values will pull colours from the left or up. The pixel at (25%,30%) will take its color from 10% to the left and 15% down, and so on.
%IMG7%magick ^ -size %WW%x%HH% xc:None ^ -fill rgb(40%%,65%%,50%%) -draw "point %%[fx:w*0.25],%%[fx:h*0.30]" ^ -fill rgb(60%%,40%%,50%%) -draw "point %%[fx:w*0.75],%%[fx:h*0.60]" ^ -fill rgb(45%%,55%%,50%%) -draw "point %%[fx:w*0.55],%%[fx:h*0.80]" ^ dgp_sps_map.miff-fill gray(50%%) -draw "point 0,0" ^ -fill gray(50%%) -draw "point 0,0" ^ -fill gray(50%%) -draw "point 0,1" ^ -fill gray(50%%) -draw "point 0,2" ^ -fill gray(50%%) -draw "point 1,0" ^ -fill gray(50%%) -draw "point 2,0" ^ -fill gray(50%%) -draw "point %%[fx:w-1],0" ^ -fill gray(50%%) -draw "point 0,%%[fx:h-1]" ^ -fill gray(50%%) -draw "point %%[fx:w-1],%%[fx:h-1]" ^ -shave 1x1 ^ -compose SrcIn -bordercolor gray(50%%) -border 1 ^ -compose Over ^
The script clut2txt3c.bat reads an image and writes a text file of pixels that are not fully transparent. Each line contains space-separated values: x y r g b. The coordinates are integers. The colour values are floating-point, typically between zero and QuantumRange. The input text file can list the pixels in any order, but must not contain duplicate (x,y) values, even if they have the same colours. Any duplicate (x,y) values will cause problems for gnuplot, which will then output garbage values.
The script is slow, about three pixels/second, so should not generally be used for hundreds of opaque pixels.
call %PICTBAT%clut2txt3c dgp_sps_map.miff dgp_sps.lis if ERRORLEVEL 1 exit /B 1
The text file dgp_sps.lis is:
67 70 26207.60009765625 42591.35009765625 32767.5 200 140 39327.3974023819 26207.60009765625 32767.5 147 186 29487.55004882813 36031.4501953125 32767.5
[
Set border pixels in the text file to 50% gray:
for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "MID=%%[fx:QuantumRange/2]\n" ^ xc:`) do set %%L rem call %PICTBAT%mSparseBord %WW%x%HH% dgp_sps.lis "%MID% %MID% %MID%" rem sort -g --key=1 dgp_sps.lis |sort -g --key=2 -s |sort --merge --unique -o dgp_sps2.lis rem del dgp_sps.lis rem ren dgp_sps2.lis dgp_sps.lis
gnuplot should be given unique values of (x,y). If dumplicate values are given, blah.
]
The script smoothSparse3c.bat reads the text file from the previous scale and writes up to three text files (one per channel) with missing values filled in. It does this by creating a script for gnuplot, and running gnuplot with that script. We will read the values with PGM, so we tell gnuplot not to write fractions. In this application, we process only the first two channels.
call %PICTBAT%smoothSparse3c dgp_sps.lis dgp_fld.lis 2 if ERRORLEVEL 1 exit /B 1
The script txt2clut3c.bat reads the three text files, using IM's PGM loader for each channel, and combines the three images. The PGM loader goes wrong wth negative or fractional numbers because the "-" and "." characters are interpreted as end-of-field.
call %PICTBAT%txt2clut3c dgp_fld.lis dgp_map.miff %WW%x%HH% extend 2 if ERRORLEVEL 1 exit /B 1
The filled map looks like this:
dgp_map.miff |
We apply the map in the usual way:
%IMG7%magick ^ %SRC% ^ dgp_map.miff ^ -compose Displace ^ -set option:compose:args 100%%x100%% ^ -composite ^ dgp_disp.png |
We use a similar method to that shown in Displacement by masked SRT. But this page uses relative displacement maps, so we mask-blend with an identity relative displacement map which is simply a 50% gray image.
First we make a mask that is white where we want the full effect, black where we want no effect, and gray inbetween.
Make a black/white/other image. %IMG7%magick ^ %SRC% ^ -fill #88f -colorize 100 ^ -shave 1x1 ^ -bordercolor Black -border 1 ^ -fill White ^ -draw "point %%[fx:w*0.25],%%[fx:h*0.30]" ^ -draw "point %%[fx:w*0.75],%%[fx:h*0.60]" ^ -draw "point %%[fx:w*0.55],%%[fx:h*0.80]" ^ dgp_mask.png |
|
Apply fanBW. call %PICTBAT%fanBW ^ dgp_mask.png ^ dgp_mask_f.png |
Blend the map with an identity map, using the mask dgp_mask_f.png:
%IMG7%magick ^ dgp_map.miff ^ ( +clone ^ -fill gray(50%%) -colorize 100 ^ ) ^ +swap ^ dgp_mask_f.png ^ -compose Over -composite ^ dgp_map_b.miff |
Apply the blended map:
%IMG7%magick ^ %SRC% ^ dgp_map_b.miff ^ -compose Displace ^ -set option:compose:args 10%%x10%% ^ -composite ^ dgp_disp2.png |
An alternative is to make the mask using a white polygon from the points. Then the area within that polygon will be fully distorted.
Make a black/white/other image. set POLYG=^ %%[fx:w*0.25],%%[fx:h*0.30],^ %%[fx:w*0.75],%%[fx:h*0.60],^ %%[fx:w*0.55],%%[fx:h*0.80] %IMG7%magick ^ %SRC% ^ -fill #88f -colorize 100 ^ -shave 1x1 ^ -bordercolor Black -border 1 ^ -fill White ^ -draw "polygon %POLYG%" ^ dgp_maskp.png |
|
Apply fanBW. call %PICTBAT%fanBW ^ dgp_maskp.png ^ dgp_maskp_f.png |
|
Blend the map with an identity map. %IMG7%magick ^ dgp_map.miff ^ ( +clone ^ -fill gray(50%%) -colorize 100 ^ ) ^ +swap ^ dgp_maskp_f.png ^ -compose Over -composite ^ dgp_map_bp.miff |
|
Apply the blended map: %IMG7%magick ^ %SRC% ^ dgp_map_bp.miff ^ -compose Displace ^ -set option:compose:args 100%%x100%% ^ -composite ^ dgp_disp2p.png |
To see the differences more clearly, we make a GIF from the source and results:
%IMG7%magick ^ -pointsize 30 ^ -fill White ^ -delay 50 ^ -gravity NorthWest ^ ( %SRC% -annotate 0,0 "SRC" ) ^ ( dgp_disp.png -annotate 0,0 "disp" ) ^ ( dgp_disp2.png -annotate 0,0 "disp2" ) ^ ( dgp_disp2p.png -annotate 0,0 "disp2p" ) ^ dgp_disp.gif |
For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.
rem Given %1 is 3-channel image Nx1 with alpha, rem writes %2 text file of non-transparent pixels. rem Each output line has five numbers: rem x-coordinate, y-coordinate, and number generally 0 to Quantum for R, G and B. rem See also txt2clut3c.bat @if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion @call echoOffSave call %PICTBAT%setInOut %1 c2t3 if not "%2"=="" if not "%2"=="." set OUTFILE=%2 for /F "usebackq" %%L in (`%IMG7%magick identify ^ -precision 16 ^ -format "QPC=%%[fx:QuantumRange/100]\nQ255=%%[fx:QuantumRange/255]\n" ^ xc:`) do set %%L :: Colour may have % suffix, or be integer in scale 0-255. ( for /F "usebackq tokens=1,2,4,5,6 delims=,() " %%A in (`%IMG7%magick ^ %INFILE% ^ -precision 16 ^ sparse-color:- ^| sed -e 's/ /\n/g' -e 's/%%/c/g' `) do ( call :calcVal "%%C" set VALR=!VAL! call :calcVal "%%D" set VALG=!VAL! call :calcVal "%%E" set VALB=!VAL! echo %%A %%B !VALR! !VALG! !VALB! ) ) >%OUTFILE% call echoRestore @endlocal & set c2t3OUTFILE=%OUTFILE% @exit /B 0 ::-------------------------------------------- :: Subroutines :calcVal set VAL=%~1 set LASTCH=!VAL:~-1! if !LASTCH!==c ( set VAL=!VAL:~0,-1! for /F "usebackq" %%L in (`%IMG7%magick identify ^ -precision 16 ^ -format "%%[fx:!VAL!*%QPC%]" ^ xc:`) do set VAL=%%L ) else ( for /F "usebackq" %%L in (`%IMG7%magick identify ^ -precision 16 ^ -format "%%[fx:!VAL!*%Q255%]" ^ xc:`) do set VAL=%%L ) exit /B 0
rem %1 input text file of x, y, v for sparse integer values of x and y. rem %2 output text file of x, y, v for all x and y from first to last. rem %3 number of channels, 1 to 3 [default 3] @if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion @call echoOffSave set INFILE=%1 set NumChan=%3 if "%NumChan%"=="." set NumChan= if "%NumChan%"=="" set NumChan=3 :: Note: gnuplot can't handle F: etc in file path. set OUTFILE0=%~n2_0%~x2 set OUTFILE1=%~n2_1%~x2 set OUTFILE2=%~n2_2%~x2 echo %0: %OUTFILE0% %OUTFILE1% %OUTFILE2% set TMPSCR=\temp\ss3c_gp_script.gp rem type %INFILE% set nFIRSTx= set nLASTx= set nFIRSTy= set nLASTy= for /F "tokens=1,2 eol=# delims=, " %%A in (%INFILE%) do ( if "!nFIRSTx!"=="" set nFIRSTx=%%A if "!nLASTx!"=="" set nLASTx=%%A if !nFIRSTx! GTR %%A set nFIRSTx=%%A if !nLASTx! LSS %%A set nLASTx=%%A if "!nFIRSTy!"=="" set nFIRSTy=%%B if "!nLASTy!"=="" set nLASTy=%%B if !nFIRSTy! GTR %%B set nFIRSTy=%%B if !nLASTy! LSS %%B set nLASTy=%%B ) echo %0: X from %nFIRSTx% to %nLASTx% echo %0: Y from %nFIRSTy% to %nLASTy% if "%nFIRSTx%"=="" exit /B 1 set /A nSAMPSx=%nLASTx%-%nFIRSTx%+1 set /A nSAMPSy=%nLASTy%-%nFIRSTy%+1 echo %0: samps: %nSAMPSx% %nSAMPSy% :: We tell gnuplot not to use decimals. :: FIXME: gnuplot can create negative numbers, which barf IM's PNM reader. ( echo set format "%%.0f" echo set dgrid3d %nSAMPSy%,%nSAMPSx% splines echo set table "%OUTFILE0%" echo splot '%INFILE%' using 1:2:3 if %NumChan% GTR 1 ( echo set table "%OUTFILE1%" echo splot '%INFILE%' using 1:2:4 if %NumChan% GTR 2 ( echo set table "%OUTFILE2%" echo splot '%INFILE%' using 1:2:5 ) ) echo unset table echo unset dgrid3d ) >%TMPSCR% type %TMPSCR% gnuplot %TMPSCR% if ERRORLEVEL 1 ( echo %0: gnuplot failed exit /B 1 ) call echoRestore endlocal & set ss3cOUTFILE=%OUTFILE%& ^ set ss3cFirstX=%nFIRSTx%& ^ set ss3cLastX=%nLASTx%& ^ set ss3cFirstY=%nFIRSTy%& ^ set ss3cLastY=%nLASTy%
rem Given %1 is three text files, rem each with x, y, v rem x,y are integers rem v is floating point, typically 0 to QuantumRange rem makes three grayscale images, rem and combines them into %2 output image. rem %3 desired width "x" height of output, eg 600x400 rem %4 "extend" or "noextend". rem %5 number of channels, 1 to 3 [default 3] @if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion @call echoOffSave call %PICTBAT%setInOut %1 c2x if not "%2"=="" if not "%2"=="." set OUTFILE=%2 set WxH=%3 if "%WxH%"=="." set WxH= set doExt=%4 if "%doExt%"=="." set doExt= if "%doExt%"=="" set doExt=noextend set NumChan=%5 if "%NumChan%"=="." set NumChan= if "%NumChan%"=="" set NumChan=3 call parseXxY 100 100 %WxH% set reqW=%pxxyX% set reqH=%pxxyY% set INFILE0=%~n1_0%~x1 set INFILE1=%~n1_1%~x1 set INFILE2=%~n1_2%~x1 set TMPDIR=\temp\ set TMPTEXT=\temp\t2c3c.txt echo %0: ss3cFirstX=%ss3cFirstX% set /A WW=%ss3cLastX%-%ss3cFirstX%+1 set /A HH=%ss3cLastY%-%ss3cFirstY%+1 if %doExt%==extend ( set EXTDIST=-virtual-pixel Edge ^ -set option:distort:viewport %reqW%x%reqH%-%ss3cFirstX%-%ss3cFirstY% ^ -distort SRT 1,0 +repage ) else ( set EXTDIST= ) for /F "usebackq" %%A in (`%IMG7%magick identify ^ -precision 19 ^ -format "%%[fx:QuantumRange]" ^ xc:`) do set QR=%%A call :mkGrayImg %INFILE0% x0.miff if ERRORLEVEL 1 exit /B 1 set IN1=( +clone -fill gray(50%%) -colorize 100 ) set IN2=( +clone -fill gray(50%%) -colorize 100 ) if %NumChan% GTR 1 ( set IN1=x1.miff call :mkGrayImg %INFILE1% !IN1! if ERRORLEVEL 1 exit /B 1 if %NumChan% GTR 2 ( set IN2=x2.miff call :mkGrayImg %INFILE2% !IN2! if ERRORLEVEL 1 exit /B 1 ) ) %IMG7%magick ^ x0.miff ^ %IN1% ^ %IN2% ^ -combine ^ %OUTFILE% if ERRORLEVEL 1 exit /B 1 echo %0: OUTFILE=%OUTFILE% call echoRestore @endlocal & set c2xOUTFILE=%OUTFILE% @exit /B 0 ::------------------------------------------ :: Subroutines :mkGrayImg set InText=%1 set OutImg=%2 echo %0: InText %InText% set SORTED=\temp\t2c_%~n1.srt set SORTED2=\temp\t2c2_%~n1.srt set SORTED3=\temp\t2c3_%~n1.srt ( echo P2 echo %WW% %HH% echo %QR% ) >%SORTED2% :: Strip comments and empty lines; sort on y then x. :: grep: we double the ^ for Windows. grep -v -e '^^[[:space:]]*$' -e '^^#' %InText% |sort -g --key=1 |sort -g --key=2 -s -o %SORTED% :: Get the third number. Also change "-0" to "0". sed -e 's/ / /g' -e 's/-0/0/g' %SORTED% |cut -d ' ' -f 3 >>%SORTED2% :: Clamp negative values to 0. ( for /F "tokens=*" %%L in (%SORTED2%) do if %%L LSS 0 (echo 0) else echo %%L ) >%SORTED3% goto skipSlow :: Next is slow: we call identify to calculate for every pixel. ( echo # ImageMagick pixel enumeration: %WW%,%HH%,%QR%,graya for /F "eol=# tokens=1-3 delims=, " %%A in (%SORTED%) do ( for /F "usebackq" %%L in (`%IMG7%magick identify ^ -precision 19 ^ -format "VAL=%%[fx:%%C*QuantumRange/100]" ^ xc:`) do set %%L echo %%A,%%B: ^(!VAL!,%QR%^) ) ) >%TMPTEXT% :skipSlow rem echo %0: TMPTEXT %TMPTEXT% rem type %TMPTEXT% rem Don't read from TXT:%TMPTEXT% ^ %IMG7%magick ^ PNM:%SORTED3% ^ -crop %WW%x%HH%+%ss3cFirstX%+%ss3cFirstY% +repage ^ %EXTDIST% ^ %OutImg% if ERRORLEVEL 1 exit /B 1 exit /B 0
rem %1 input with back, white and other. rem %2 output, replacing "other" with graduated gray. rem Method: fan composition. set INFILE=%1 set OUTFILE=%2 goto skip %IMG7%magick ^ %INFILE% ^ -alpha off ^ ( -clone 0 ^ -channel RGB -negate +channel ^ -fill White +opaque Black ^ -morphology Distance Euclidean:4 ^ -channel RGB -negate +channel ^ -auto-level ^ ) ^ ( -clone 0 ^ -fill White +opaque Black ^ -morphology Distance Euclidean:4 ^ -auto-level ^ ) ^ -delete 0 ^ ( -clone 1 -evaluate Divide 2 ) ^ ( -clone 0-1 ^ -compose Mathematics ^ -define compose:args=0,0.5,-0.5,0.5 ^ -composite ^ ) ^ -delete 0-1 ^ -compose DivideSrc -composite ^ %OUTFILE% :skip %IMG7%magick ^ %INFILE% ^ -alpha off ^ ( -clone 0 ^ -channel RGB -negate +channel ^ -fill White +opaque Black ^ -morphology Distance Euclidean:4 ^ -channel RGB -negate +channel ^ -auto-level ^ ) ^ ( -clone 0 ^ -fill White +opaque Black ^ -morphology Distance Euclidean:4 ^ -auto-level ^ -evaluate Divide 2 ^ ) ^ -delete 0 ^ ( -clone 0-1 ^ -compose Mathematics ^ -define compose:args=0,1,-0.5,0.5 ^ -composite ^ ) ^ -delete 0 ^ -compose DivideSrc -composite ^ %OUTFILE%
All images on this page were created by the commands shown, using:
%IM%identify -version
Version: ImageMagick 6.9.9-50 Q16 x64 2018-06-02 http://www.imagemagick.org 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 gslib heic jng jp2 jpeg lcms lqr lzma openexr pangocairo png ps raw rsvg tiff webp xml zlib
To improve internet download speeds, some images may have been automatically converted (by ImageMagick, of course) from PNG to JPG.
Source file for this web page is dispplot.h1. To re-create this web page, execute "procH1 dispplot".
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-August-2019.
Page created 04-Aug-2019 20:49:51.
Copyright © 2019 Alan Gibson.