blah.
blah
This page assumes that %IMDEV%convert is built with my process modules, and has FFTW, and is an HDRI (floating-point) build. It is also Q32, though this is probably not important.
The basic idea of convolution is fairly simple. We start with two images: a source and a kernel. Each pixel in the result is a weighted average of pixels near the corresponding source pixel, where the kernel defines the weighting.
Explanations of convolution often ignore colour and transparency. This page considers both.
Methods:
Source images:
toes.png |
|
%IMG7%magick ^ toes.png ^ ( toes_holed.png ^ -alpha extract ^ -negate ^ -morphology erode disk:4 ^ ) ^ -compose CopyOpacity -composite ^ cnv_toes_t.png |
Kernels:
Code | Kernel | Enlarged kernel |
---|---|---|
%IMG7%magick ^ -size 20x20 ^ label:L ^ -auto-level ^ -threshold 50%% ^ -trim +repage ^ -negate ^ cnv_kl.png set bpDO_CIRCLE=1 set bpCIRC_X=0 set bpCIRC_Y=11 set bpDO_CROSS=1 set bpCROSS_X=3 set bpCROSS_Y=5 call %PICTBAT%blockPix ^ cnv_kl.png "X" and "O" are possible origins. |
||
%IMG7%magick ^ cnv_kl.png ^ -transparent Black ^ cnv_klt.png call %PICTBAT%blockPix ^ cnv_klt.png |
||
%IMG7%magick ^ -size 9x9 xc:black +antialias ^ -fill white -draw "line 0,0 8,8" ^ gradient: ^ -compose Multiply -composite ^ cnv_kl2.png set bpCIRC_X=0 set bpCIRC_Y=0 set bpCROSS_X=4 set bpCROSS_Y=4 call %PICTBAT%blockPix ^ cnv_kl2.png |
||
%IMG7%magick ^ -size 1x9 gradient: ^ ( +clone -negate ) ^ ( +clone -fill White -colorize 100 ) ^ -combine ^ cnv_kl3.png set bpCIRC_X=0 set bpCIRC_Y=0 set bpCROSS_X=0 set bpCROSS_Y=4 call %PICTBAT%blockPix ^ cnv_kl3.png set bpDO_CIRCLE=0 set bpDO_CROSS=0 |
Impulse images (for source or kernel):
Code | Kernel | Enlarged kernel |
---|---|---|
%IMG7%magick ^ xc:White ^ cnv_imp1.png call %PICTBAT%blockPix ^ cnv_imp1.png |
||
%IMG7%magick ^ xc:White ^ -bordercolor Black -border 1 ^ cnv_imp2.png call %PICTBAT%blockPix ^ cnv_imp2.png |
||
%IMG7%magick ^ -size 3x3 xc:Black ^ -fill White ^ -draw "point 0,0 point 2,2" ^ cnv_imp3.png call %PICTBAT%blockPix ^ cnv_imp3.png |
Coloured impulse images (for source or kernel):
Code | Kernel | Enlarged kernel |
---|---|---|
%IMG7%magick ^ xc:#02e ^ cnv_cimp1.png call %PICTBAT%blockPix ^ cnv_cimp1.png |
||
%IMG7%magick ^ xc:#02e ^ -bordercolor Black -border 1 ^ cnv_cimp2.png call %PICTBAT%blockPix ^ cnv_cimp2.png |
||
%IMG7%magick ^ -size 3x3 xc:Black ^ -fill #02e ^ -draw "point 0,0" ^ -fill #e02 ^ -draw "point 2,2" ^ cnv_cimp3.png call %PICTBAT%blockPix ^ cnv_cimp3.png |
||
%IMG7%magick ^ -size 3x3 xc:None ^ -fill #02e ^ -draw "point 0,0" ^ -fill #e02 ^ -draw "point 2,2" ^ cnv_cimp4.png call %PICTBAT%blockPix ^ cnv_cimp4.png |
Blurring kernels:
Code | Kernel | Enlarged kernel |
---|---|---|
%IMG7%magick ^ cnv_imp2.png ^ -blur 0x1 -auto-level ^ cnv_blr2d.png call %PICTBAT%blockPix ^ cnv_blr2d.png |
||
%IMG7%magick ^ cnv_blr2d.png ^ -crop 1x3+1+0 +repage ^ cnv_blr1d.png call %PICTBAT%blockPix ^ cnv_blr1d.png |
Sadly, IM commands that use kernels need strings, not images. Moreover, kernels have only one channel, and the weighting is binary: each element is considered either entirely present or entirely absent. (Why these restrictions? I don't know. Simplicity, perhaps.) Values of 0.0 correspond to black, and 1.0 (unlike most of IM) correspond to white.
We can convert an image to a kernel string with my process module img2knl.
for /F "usebackq" %%L in (`%IM7DEV%magick ^ cnv_kl.png ^ -process img2knl ^ NULL:`) do set KNL_KL=%%L echo %KNL_KL%
6x11:1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,1,-,-,-,-,1,1,1,1,1,1
for /F "usebackq" %%L in (`%IM7DEV%magick ^ cnv_klt.png ^ -process img2knl ^ NULL:`) do set KNL_KLT=%%L echo %KNL_KLT%
6x11:1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,-,-,-,-,-,1,1,-,-,-,-,1,1,1,1,1,1
The script knl2img.bat does the inverse, creating a kernel image from a kernel string. It works by convolving an impulse image with the kernel. The impulse image is the same size as the kernel, black except for one white pixel at same position as the origin of the kernel.
knl2img only works for single kernels, and uses "-morphology convolve" so treats kernel values "-" as "0", so the result will be fully opaque. See also the bash script kernel2image, probably written by Anthony Thyssen, which is more sophisticated.
Code | Kernel | Enlarged kernel |
---|---|---|
call %PICTBAT%knl2img ^ "%KNL_KL%" cnv_kl_inv.png call %PICTBAT%blockPix ^ cnv_kl_inv.png |
The script convolveSMA.bat correlates an image by first principles. For each pixel in the kernel, it multiplies the image by that kernel pixel, then adds this, offset by the coordinates in the kernel, to the result so far.
The operation is commutative, ie gives the same result when we swap the roles of image and kernel. [No it isn't, because of the kernel origin.]
As it runs magick and save two image files for every kernel pixel, this isn't fast. For fastest speed, put the smaller image (usually the kernel) second. The other way round is massively slower.
The resulting image is larger than the source. If the inputs are WxH and wxh, the output is (W+w-1)x(H+h-1).
Simple examples, with "O" marking the kernel origin:
Blurring kernels:
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_blr1d.png ^ cnv_sma_b1d.png 0 0 |
|
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_blr1d.png ^ cnv_sma_b1dk.png 0 0 knl |
|
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_blr2d.png ^ cnv_sma_b2d.png 0 0 |
|
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_blr2d.png ^ cnv_sma_b2dk.png 0 0 knl |
|
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_blr2d.png ^ cnv_sma_b2d1.png 1 1 |
Other kernels:
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_imp1.png ^ cnv_sma1.png 0 0 |
|
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_cimp3.png ^ cnv_sma2.png 0 0 |
|
call %PICTBAT%convolveSmaBp ^ cnv_cimp3.png cnv_kl3.png ^ cnv_sma3.png 0 0 |
|
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_cimp3.png ^ cnv_sma4.png 1 1 |
|
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_blr2d.png ^ cnv_sma5.png 0 0 none |
|
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_blr2d.png ^ cnv_sma6.png 1 1 none |
|
call %PICTBAT%convolveSmaBp ^ cnv_kl2.png cnv_kl3.png ^ cnv_sma7.png 0 0 none |
|
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_kl3.png ^ cnv_sma8.png 0 0 none |
|
call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_kl3.png ^ cnv_sma9.png 0 0 knl |
|
%IMG7%magick ^ cnv_kl3.png ^ -rotate -90 ^ cnv_kl3r.png call %PICTBAT%convolveSmaBp ^ cnv_kl3.png cnv_kl3r.png ^ cnv_sma10.png 0 0 none |
The simple examples above illustrate:
Convolutions of photographs:
call %PICTBAT%convolveSMA ^ toes.png cnv_kl.png cnv_tkl.png . . knl |
|
rem call %PICTBAT%convolveSMA ^ rem cnv_kl.png toes.png cnv_tkl2.png . . knl |
|
call %PICTBAT%convolveSMA ^ toes.png cnv_klt.png cnv_tklt.png . . knl |
Enlarging the image by the size of the kernel is, in general, a reasonable action. The image might be a solid-colour rectangle and the kernel might be a blurring filter, so we would expect the result to be larger (in the same way that adding a shadow extends the image).
However, we may want to crop the image to the size of the source. The crop offsets will be the kernel origin.
%IMG7%magick ^ cnv_tkl.png ^ -crop 267x233+3+5 +repage ^ cnv_tkl_c1.png |
|
%IMG7%magick ^ cnv_tkl.png ^ -crop 267x233+0+11 +repage ^ cnv_tkl_c2.png |
The kernel doesn't take full effect until the entire kernel is over the image. In cnv_tkl.png, the left-most and right-most 6 columns, and top and bottom 11 rows, are dark. If we trimmed all these off, the result would be smaller than either the source or the kernel. It would be (W-w+1)x(H-h+1) pixels.
A workaround is to extend the image with -distort SRT 0 and a viewport, and virtual pixel edge, which is the default. For simplicity, we extend the width by twice the kernel width, and the height by twice the kernel height.
Extend the source image. call %PICTBAT%getImgWH toes.png src_dims call %PICTBAT%getImgWH cnv_kl.png knl_dims set sFMT=sVP=^ %%[fx:%src_dims_WW%+2*%knl_dims_WW%]x^ %%[fx:%src_dims_HH%+2*%knl_dims_HH%]-^ %knl_dims_WW%-^ %knl_dims_HH% for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "%sFMT%" ^ xc:`) do set %%L echo sVP=%sVP% %IMG7%magick ^ toes.png ^ -define distort:viewport=%sVP% ^ -distort SRT 0 +repage ^ cnv_toes_ext.png echo sVP=%sVP% sVP=279x255-6-11 |
|
Convolve the extended source. call %PICTBAT%convolveSMA ^ cnv_toes_ext.png cnv_kl.png cnv_tkl_e.png . . knl |
|
Crop the convolution. set /A OFFS_X=%knl_dims_WW%+0 set /A OFFS_Y=%knl_dims_HH%+11 %IMG7%magick ^ cnv_tkl_e.png ^ -crop ^ %src_dims_WW%x%src_dims_HH%+%OFFS_X%+%OFFS_Y% ^ +repage ^ cnv_tkl_ec.png |
The result is the same size as the source, with no edge effects.
We may create a wrapper around convolveSMA.bat that first extends the source, and lastly crops the result. It needs to know the kernel origin.
Instead of convolving and then cropping to an offset that represents the kernel origin, we could instead first roll the kernel to place the origin top-left, then convolve, and then the final crop doesn't need an offset. [But this seems to change the nature of the blur?? blah]
Commands:
-convolve {kernel}. An old form of -morphology convolve {kernel}.
-morphology convolve {kernel}. This never changes the alpha channel. Processes "-" and "0" in a kernel string in the same way.
(These are different.)
-morphology correlate {kernel}
%IMG7%magick ^ toes.png ^ -convolve %KNL_KL% ^ cnv_kl_c1.png |
|
%IMG7%magick ^ toes.png ^ -morphology convolve %KNL_KL% ^ cnv_kl_mc1.png |
|
%IMG7%magick ^ toes.png ^ -define "convolve:scale=^!" ^ -morphology convolve %KNL_KL% ^ cnv_kl_mc2.png |
IM can scale kernels with:
-define convolve:scale={kernel_scale}[!^] [,{origin_addition}] [%]
The normalisation flags "^" and "!" are applied first. Then values are multiplied by {kernel_scale}, and finally {origin_addition} is added to just the value at the origin. There is no facility for adding a number to all the kernel values.
The flags "^" and "!" tell IM to normalize the kernel. If the values are all positive (possibly including zero), the flags do the same thing: they scale the values to add to +1.0. Likewise, if the values are all negative (possibly including zero), both flags scale the result so they add to -1.0.
When the kernel has both positive and negative values, the flags operate differently. "^" scales the positive values to add to +1.0 and independently scales the negative values to -1.0. The overall kernel will then add to 0.0.
By contrast, the "!" flag first adds the values. If they already add to zero, it operates as "^", independently scaling positive and negative values so they add to +1.0 and -1.0, and the overall kernel adds to 0.0. If the values don't already add to zero, it scales both positive and negative values together so the overall sum is +1.0.
Examples with the "^" flag:
%IMG7%magick xc: ^ -define morphology:showkernel=1 ^ -define "convolve:scale=^^" ^ -morphology Convolve 4x1:0,2,3,8 ^ NULL:
Kernel "User Defined" of size 4x1+1+0 with values from 0 to 0.615385 Forming a output range from 0 to 1 (Normalized) 0: 0 0.153846 0.230769 0.615385
%IMG7%magick xc: ^ -define morphology:showkernel=1 ^ -define "convolve:scale=^^" ^ -morphology Convolve 4x1:0,-2,-3,-8 ^ NULL:
Kernel "User Defined" of size 4x1+1+0 with values from -0.615385 to 0 Forming a output range from -1 to 0 (Sum -1) 0: 0 -0.153846 -0.230769 -0.615385
%IMG7%magick xc: ^ -define morphology:showkernel=1 ^ -define "convolve:scale=^^" ^ -morphology Convolve 4x1:0,-2,-3,8 ^ NULL:
Kernel "User Defined" of size 4x1+1+0 with values from -0.6 to 1 Forming a output range from -1 to 1 (Zero-Summing) 0: 0 -0.4 -0.6 1
%IMG7%magick xc: ^ -define morphology:showkernel=1 ^ -define "convolve:scale=^^" ^ -morphology Convolve 4x1:0,-2,-3,5 ^ NULL:
Kernel "User Defined" of size 4x1+1+0 with values from -0.6 to 1 Forming a output range from -1 to 1 (Zero-Summing) 0: 0 -0.4 -0.6 1
IM v6 works with or without the "morphology:" prefix in "morphology:showkernel=1", but v7 needs the prefix.
Examples with the "!" flag:
%IMG7%magick xc: ^ -define morphology:showkernel=1 ^ -define "convolve:scale=^!" ^ -morphology Convolve 4x1:0,2,3,8 ^ NULL:
Kernel "User Defined" of size 4x1+1+0 with values from 0 to 0.615385 Forming a output range from 0 to 1 (Normalized) 0: 0 0.153846 0.230769 0.615385
%IMG7%magick xc: ^ -define morphology:showkernel=1 ^ -define "convolve:scale=^!" ^ -morphology Convolve 4x1:0,-2,-3,-8 ^ NULL:
Kernel "User Defined" of size 4x1+1+0 with values from -0.615385 to 0 Forming a output range from -1 to 0 (Sum -1) 0: 0 -0.153846 -0.230769 -0.615385
%IMG7%magick xc: ^ -define morphology:showkernel=1 ^ -define "convolve:scale=^!" ^ -morphology Convolve 4x1:0,-2,-3,8 ^ NULL:
Kernel "User Defined" of size 4x1+1+0 with values from -1 to 2.66667 Forming a output range from -1.66667 to 2.66667 (Normalized) 0: 0 -0.666667 -1 2.66667
%IMG7%magick xc: ^ -define morphology:showkernel=1 ^ -define "convolve:scale=^!" ^ -morphology Convolve 4x1:0,-2,-3,5 ^ NULL:
Kernel "User Defined" of size 4x1+1+0 with values from -0.6 to 1 Forming a output range from -1 to 1 (Zero-Summing) 0: 0 -0.4 -0.6 1
-define morphology:showkernel=1
-bias {value}[%] an old form of -define convolve:bias={value}.
-define convolve:bias={value} Adds {value} to the output of the convolution.
We sometimes want a kernel to sum to one or zero.
Zero-sum:
-define convolve:scale=50%\! -define convolve:bias=50%
If the kernel origin is not specified in the string, it defaults to the mid-coordinate. When the dimension is odd, this is simply the middle pixel:
%IMG7%magick ^ xc: ^ -define morphology:showkernel=1 ^ -morphology Convolve 3x3:1,2,3,4,5,6,7,8,9 ^ NULL:
Kernel "User Defined" of size 3x3+1+1 with values from 1 to 9 Forming a output range from 0 to 45 (Sum 45) 0: 1 2 3 1: 4 5 6 2: 7 8 9
However, when the dimension is even, this is the coordinate to the up-and-left of the centre lines. I'm not sure if this is wise, for FFT transformations.
%IMG7%magick ^ xc: ^ -define morphology:showkernel=1 ^ -morphology Convolve 4x4:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 ^ NULL:
Kernel "User Defined" of size 4x4+1+1 with values from 1 to 16 Forming a output range from 0 to 136 (Sum 136) 0: 1 2 3 4 1: 5 6 7 8 2: 9 10 11 12 3: 13 14 15 16
Create an icon. %IMG7%magick ^ -size 100x100 xc:None ^ -fill #02e ^ -draw "polygon 50,0 0,99 99,99" ^ -fill #f80 ^ -draw "circle 30,70 5,70" ^ cnv_col_knl.png |
|
Create points for the origins of icons. set sPOINTS=^ point 100,100 ^ point 300,120 ^ point 320,120 ^ point 320,220 ^ point 400,375 ^ point 500,25 %IMG7%magick ^ -size 600x400 xc:Black ^ -fill White ^ -draw "%sPOINTS%" ^ -fill #808080 ^ -draw "point 120,250" ^ -fill #f00 ^ -draw "point 500,200" ^ -fill #00f ^ -draw "point 500,300" ^ -fill gray(4%%) ^ -draw "rectangle 201,301,205,305" ^ cnv_points.png |
|
Create text files of the kernel. call %PICTBAT%img2knl4f ^ cnv_col_knl.png cnv_col_knl.dat |
[No image] |
Convolve with these text kernel files. %IMG7%magick ^ cnv_points.png -alpha off ^ ( -clone 0 ^ -morphology Convolve @cnv_col_knl_R.dat ) ^ ( -clone 0 ^ -morphology Convolve @cnv_col_knl_G.dat ) ^ ( -clone 0 ^ -morphology Convolve @cnv_col_knl_B.dat ) ^ ( -clone 0 ^ -morphology Convolve @cnv_col_knl_A.dat ) ^ -delete 0 ^ -channel RGBA -combine ^ cnv_icons.png |
Where icons overlap, their values (in all four channels) are added.
Where a point isn't white, the values in the icon are reduced.
Colouring pixels in cnv_points.png makes no difference; it doesn't change colours in the result. Non-white pixels in cnv_points.png reduce kernel values in all channels by the same proportion.
We encapulate this in a script, convolveMC.bat. Blah. This writes four text files from the kernel, convolves with them, and combines the results.
We can convolve a source image by:
"-complex multiply" needs the real-and-imaginary form of FFT, so we use "+fft" and "+ift".
"-complex multiply" needs the four inputs (i.e. two complex images) to have the same dimensions. "+fft" would make its input square, with the dimension being a multiple of 2, if it wasn't already. But that would result in two different sizes being used, so we extend the source and kernel images to the same square dimension, before we transform them.
What should that dimension be? It should be the maximum dimension of the source and kernel, plus one if that isn't divisible by 2. This source (toes.png) is 267x233. The kernel (cnv_kl.png) is 7x12. The maximum dimension is 267, which isn't divisible by 2, so we use 268.
[An alternative: maximum of (W+w-1) and (H+h-1), 273 and 244, so max is 273, plus 1 is 274.]
By default, "+fft" normalises the result by dividing by W*H. We could override this default by blah, but instead for this example we multiply the result by W*H = 268*268 = 71824.
A blurring kernel should generally be normalised so the pixels sum to 1.0, so the overall intensity of the result will be that of the source. The kernel cnv_kl.png has 29 white pixels and the rest are black, so it sums to 29. So we normalise in this example by dividing the kernel by 29.
Instead of dividing the kernel by 29, we could divide the source by 29 or the result by 29. The effect would be the same.
Note that multiplying by W*H and dividing by the sum of the kernel is the same as dividing by the mean of the kernel after it has been extended.
[Explain the roll. After extending the kernel, we roll it to place the kernel origin at image coordinate 0,0, the top-left corner.]
%IMDEV%convert ^ -gravity Center ^ -background Black ^ ( toes.png ^ -extent 274x274 ^ +fft ^ ) ^ ( cnv_kl.png ^ -evaluate Divide 29 ^ -extent 274x274 ^ -roll -137-137 ^ +fft ^ ) ^ -complex multiply ^ +ift ^ -evaluate Multiply 71824 ^ cnv_cf1.png |
We need to crop. As with the shift-multiply-add method, no crop entirely removes the edge effect. blah
Crop the convolution. %IMG7%magick ^ cnv_cf1.png ^ -crop 267x233+2+21 ^ +repage ^ cnv_cf1_c.png |
As before, we can extend the source by twice the width and height of the kernel. But the image needs to be square, with the dimension a multiple of two.
267+2*7 = 281 233+2*12 = 257
The largest of these is 281, so we will use 282 as the dimension. The viewport offsets will be half the number of added pixels:
(282-267)/2 = 7 (282-233)/2 = 24
For the crop offsets, we add the kernel origin. [Wrong. It's more complex?]
7 + 3 = 10 24 + 5 = 29
%IMDEV%convert ^ -background Black ^ ( toes.png ^ -define distort:viewport=282x282-7-24 ^ -distort SRT 0 +repage ^ +write cnv_cf2_t.png ^ +fft ^ ) ^ ( cnv_kl.png ^ -evaluate Divide 29 ^ -gravity Center ^ -extent 282x282 ^ -roll -141-141 ^ +fft ^ ) ^ -complex multiply ^ +ift ^ -evaluate Multiply 79524 ^ cnv_cf2.png |
|
Crop the convolution. %IMG7%magick ^ cnv_cf2.png ^ -crop 267x233+10+29 ^ +repage ^ cnv_cf2_c.png |
[Table: methods horizontally, kernels vertically.]
Kernel | Methods | ||
---|---|---|---|
morphology convolve | shift-multiply-add | FFT complex multiply |
Given an image, and the result from a correlation (or convolution) of that image, what kernel made that result from that image?
A more difficult question: given the result from a correlation (or convolution), what is the "best" image we can make from it?
We could use some kernel scripts:
For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.
rem Given images %1 (source) and %2 (kernel), rem convolves %1 with %2 by shift-multiply-add. rem Output to %3. rem Uses HDRI, so images may contain negative values. rem %4, %5 kernel origin [default: central coordinate] rem %6 is one of rem none: no normalisation of output rem knl: normalises output by dividing by sum of (colours times alphas) in kernel. rem src: normalises output by dividing by sum of (colours times alphas) in source. rem %7 is one of rem none no extension of source rem str where str is an IM virtual-pixel setting, first extend the source. rem Bias? rem Option to rotate. rem FIXME: this is wrongly named. rem the convolution rotates the kernel by 180 degrees. rem So this is the correlation. ?? @if "%3"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion @call echoOffSave call %PICTBAT%setInOut %1 csma set KERNEL=%2 if not "%3"=="" if not "%3"=="." set OUTFILE=%3 set KO_X=%4 if "%KO_X%"=="." set KO_X= set KO_Y=%5 if "%KO_Y%"=="." set KO_Y= rem echo KO_X=[%KO_X%] KO_Y=[%KO_Y%] set NORMALISE=%6 if "%NORMALISE%"=="." set NORMALISE= if "%NORMALISE%"=="" set NORMALISE=none set EXT_METH=%7 if "%EXT_METH%"=="." set EXT_METH= if "%EXT_METH%"=="" set EXT_METH=none set nNORM=99 if /i %NORMALISE%==none set nNORM=0 if /i %NORMALISE%==knl set nNORM=1 if /i %NORMALISE%==src set nNORM=2 if %nNORM%==99 ( echo Bad normalise [%NORMALISE%] exit /B 1 ) set TMP_EXT=.miff set TMP_IN=csma_in%TMP_EXT% set TMP_OUT=csma%TMP_EXT% set TMP_PXL=csma_pix%TMP_EXT% set TMP_KNL=csma_knl%TMP_EXT% set TMP_PXL_C=csma_pix_c%TMP_EXT% set TMP_PXL_A=csma_pix_a%TMP_EXT% set SRC_WW= for /F "usebackq" %%L in (`%IM%identify ^ -format "SRC_WW=%%w\nSRC_HH=%%h" ^ %INFILE%`) do set %%L if "%SRC_WW%"=="" exit /B 1 set K_WW= for /F "usebackq" %%L in (`%IM%convert ^ %KERNEL% ^ +write %TMP_KNL% ^ ^( +clone -background Black -alpha Background -alpha off ^ -scale "1x1^!" ^ +write %TMP_PXL_C% +delete ^) ^ ^( +clone -alpha extract ^ -scale "1x1^!" ^ +write %TMP_PXL_A% +delete ^) ^ -format "K_WW=%%w\nK_HH=%%h\nK_WH=%%[fx:w*h]\nK_Wm1=%%[fx:w-1]\nK_Hm1=%%[fx:h-1]" ^ info:`) do set %%L if "%K_WW%"=="" exit /B 1 %IM%convert %TMP_PXL_C% txt: :: FIXME: for even-numbers, more sensible to simply divide by 2? if "%KO_X%"=="" set /A KO_X=(%K_WW%-1)/2 if "%KO_Y%"=="" set /A KO_Y=(%K_HH%-1)/2 set /A ROLL_X=-%KO_X% set /A ROLL_Y=-%KO_Y% set sX= set sY= if %ROLL_X% GEQ 0 set sX=+ if %ROLL_Y% GEQ 0 set sY=+ set sROLL=-roll %sX%%ROLL_X%%sY%%ROLL_Y% if "%sROLL%"=="-roll +0+0" set sROLL= echo %0: sROLL=%sROLL% :: FIXME: We should normalise here. if not "%sROLL%"=="" %IM%convert ^ %TMP_KNL% ^ %sROLL% ^ %TMP_KNL% set sEXT= if /I not "%EXT_METH%"=="none" ( set sFMT=sVP=^ %%[fx:%SRC_WW%+2*%K_WW%]x^ %%[fx:%SRC_HH%+2*%K_HH%]-^ %K_WW%-^ %K_HH% for /F "usebackq" %%L in (`%IM%identify ^ -format "!sFMT!" ^ xc:`) do set %%L echo %0: sVP=!sVP! set sEXT=^ -virtual-pixel %EXT_METH% ^ -define distort:viewport=!sVP! ^ -distort SRT 0 +repage echo %0: sEXT=!sEXT! ) %IMDEV%convert ^ %INFILE% ^ %sEXT% ^ %TMP_IN% if ERRORLEVEL 1 exit /B 1 %IM%identify %TMP_IN% set REAL_HDRI=-define compose:clamp=off -define quantum:format=floating-point +depth %IMDEV%convert ^ -size %SRC_WW%x%SRC_HH% xc:None ^ %REAL_HDRI% ^ %TMP_OUT% :: TMP_PXL is a running total of the sum of the kernel pixels. :: %IMDEV%convert ^ -size 1x1 xc:None ^ %REAL_HDRI% ^ %TMP_PXL% for /L %%J in (0,1,%K_Hm1%) ^ do for /L %%I in (0,1,%K_Wm1%) ^ do %IMDEV%convert ^ -define compose:clamp=off ^ %TMP_OUT% ^ ^( %TMP_IN% ^ ^( %TMP_KNL% ^ -crop 1x1+%%I+%%J +repage ^ ^( +clone ^ %TMP_PXL% ^ %REAL_HDRI% ^ -compose Plus -composite ^ +write %TMP_PXL% ^ +delete ^ ^) ^ -scale "%SRC_WW%x%SRC_HH%^!" ^ ^) ^ %REAL_HDRI% ^ -channel RGBA ^ -compose Multiply -composite ^ +channel ^ -repage +%%I+%%J ^ ^) ^ %REAL_HDRI% ^ -background Black ^ -compose Plus -layers merge ^ +repage ^ %TMP_OUT% %IM%identify %TMP_OUT% set OUT_WW= for /F "usebackq" %%L in (`%IM%identify ^ -format "OUT_WW=%%w\nOUT_HH=%%h" ^ %TMP_OUT%`) do set %%L if "%OUT_WW%"=="" exit /B 1 :: FIXME: instead of normalising at the end, quicker to normalise before extending. if %nNORM%==1 ( %IMDEV%convert ^ %TMP_OUT% ^ ^( %TMP_PXL% +write txt: -scale "%OUT_WW%x%OUT_HH%^!" ^) ^ -channel RGB ^ %REAL_HDRI% ^ -compose DivideSrc -composite ^ +channel ^ %OUTFILE% ) else if %nNORM%==2 ( echo nNORM==2 NYI exit /B 1 ) else ( %IMDEV%convert ^ %TMP_OUT% ^ %OUTFILE% ) call echoRestore @endlocal & set csmaOUTFILE=%OUTFILE%& set csmaKO_X=%KO_X%& set csmaKO_Y=%KO_Y%
rem Calls convolveSMA but output (%3) is a "blockPix" of inputs and output. @if "%3"=="" exit /B 1 @setlocal @call echoOffSave call %PICTBAT%convolveSMA %1 %2 %3 %4 %5 %6 %7 if ERRORLEVEL 1 exit /B 1 set bpDO_CIRCLE=0 set bpDO_CROSS=0 call %PICTBAT%blockPix %1 csb_src.miff set bpDO_CIRCLE=1 set bpCIRC_X=%csmaKO_X% set bpCIRC_Y=%csmaKO_Y% call %PICTBAT%blockPix %2 csb_knl.miff set bpDO_CIRCLE=0 call %PICTBAT%blockPix %3 csb_out.miff %IM%convert ^ -gravity center ^ -background None ^ -pointsize 30 ^ csb_src.miff ^ pango:"⊛" ^ csb_knl.miff ^ pango:"⇒" ^ csb_out.miff ^ +append ^ %3 if ERRORLEVEL 1 exit /B 1 call echoRestore endlocal
rem Given images %1 (source) and %2 (kernel), rem convolves %1 with %2 by "-morphology convolve". rem Output to %3. rem Uses HDRI, so images may contain negative values. rem %4, %5 kernel origin [default: central coordinate] rem %6 is one of rem none: no normalisation of output rem knl: normalises output by dividing by sum of (colours times alphas) in kernel. rem src: normalises output by dividing by sum of (colours times alphas) in source. @if "%3"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion @call echoOffSave call %PICTBAT%setInOut %1 cmc set KERNEL=%2 if not "%3"=="" if not "%3"=="." set OUTFILE=%3 set KO_X=%4 if "%KO_X%"=="." set KO_X= set KO_Y=%5 if "%KO_Y%"=="." set KO_Y= rem echo KO_X=[%KO_X%] KO_Y=[%KO_Y%] set NORMALISE=%6 if "%NORMALISE%"=="." set NORMALISE= if "%NORMALISE%"=="" set NORMALISE=none set nNORM=99 if /i %NORMALISE%==none set nNORM=0 if /i %NORMALISE%==knl set nNORM=1 if /i %NORMALISE%==src set nNORM=2 if %nNORM%==99 ( echo Bad normalise [%NORMALISE%] exit /B 1 ) set sSCALE_K= if %nNORM%==1 ( set sSCALE_K=-define "convolve:scale=^^^!" ) echo sSCALE_K=[%sSCALE_K%] set TMP_EXT=.miff set TMP_IMG=cmc%TMP_EXT% set TMP_PXL=cmc_pix%TMP_EXT% set TMP_KNL=cmc_knl%TMP_EXT% set TMP_PXL_C=cmc_pix_c%TMP_EXT% set TMP_PXL_A=cmc_pix_a%TMP_EXT% set IMG_WW= for /F "usebackq" %%L in (`%IM%identify ^ -format "IMG_WW=%%w\nIMG_HH=%%h" ^ %INFILE%`) do set %%L if "%IMG_WW%"=="" exit /B 1 set K_WW= for /F "usebackq" %%L in (`%IM%convert ^ %KERNEL% ^ +write %TMP_KNL% ^ ^( +clone -background Black -alpha Background -alpha off ^ -scale "1x1^!" ^ +write %TMP_PXL_C% +delete ^) ^ ^( +clone -alpha extract ^ -scale "1x1^!" ^ +write %TMP_PXL_A% +delete ^) ^ -format "K_WW=%%w\nK_HH=%%h\nK_Wm1=%%[fx:w-1]\nK_Hm1=%%[fx:h-1]" ^ info:`) do set %%L if "%K_WW%"=="" exit /B 1 :: FIXME: for even-numbers, more sensible to simply divide by 2? if "%KO_X%"=="" set /A KO_X=(%K_WW%-1)/2 if "%KO_Y%"=="" set /A KO_Y=(%K_HH%-1)/2 set sX= set sY= if %KO_X% GEQ 0 set sX=+ if %KO_Y% GEQ 0 set sY=+ set sORIG=%sX%%KO_X%%sY%%KO_Y% call %PICTBAT%img2knl4f %KERNEL% cmc_knl.dat . %sORIG% %IM%convert ^ %INFILE% -alpha off ^ ( -clone 0 ^ -channel R -separate +channel ^ %sSCALE_K% ^ -morphology Convolve @cmc_knl_R.dat ) ^ ( -clone 0 ^ -channel G -separate +channel ^ %sSCALE_K% ^ -morphology Convolve @cmc_knl_G.dat ) ^ ( -clone 0 ^ -channel B -separate +channel ^ %sSCALE_K% ^ -morphology Convolve @cmc_knl_B.dat ) ^ ( -clone 0 ^ -channel A -separate +channel ^ %sSCALE_K% ^ -morphology Convolve @cmc_knl_A.dat ) ^ -delete 0 ^ -channel RGBA -combine ^ %OUTFILE% call echoRestore @endlocal & set cmcOUTFILE=%OUTFILE%& set cmcKO_X=%KO_X%& set cmcKO_Y=%KO_Y%
rem Given %1 a quoted kernel string rem or name of text file, prefixed with "@", rem makes image %2 of that kernel. rem %3 is optional scale parameter, format: rem {kernel_scale}[!^] [,{origin_addition}] [%] rem %4 is post-processing, eg "-auto-level". @rem @rem Updated: @rem 1-August-2022 for IM v7. Assumes magick is HDRI. @rem @if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion @call echoOffSave call %PICTBAT%setInOut %1 k2i set qKNL=%1 set sKNL=%~1 set OUTFILE=%2 set SCALE=%~3 if "%SCALE%"=="." set SCALE= set sPOST=%~4 if "%sPOST%"=="." set sPOST= if "%SCALE%"=="" ( set sSCALE= ) else ( set sSCALE=-define "convolve:scale=%SCALE%" ) if "%sPOST%"=="." set sPOST= echo sSCALE=%sSCALE% sPOST=%sPOST% :: We use an impulse image, same size as kernel, :: black but with white pixel at kernel origin. :: Sample showkernel output: :: Kernel "Blur" of size 41x1+20+0 with values from 0 to 0.0796737 :: But "User defined" (two words). set WW= set IS_FIRST=1 for /F "usebackq tokens=* eol=: delims= " %%A in (`%IMG7%magick ^ xc: ^ -define morphology:showkernel^=1 ^ -morphology convolve:0 %qKNL% ^ NULL: 2^>^&1`) do if !IS_FIRST!==1 ( set SIZE=%%A set IS_FIRST=0 ) for /F "tokens=1-4 eol=: delims=x+ " %%A in ("%SIZE:*size =%") do ( set WW=%%A set HH=%%B set X=%%C set Y=%%D ) if "%WW%"=="" exit /B 1 if %WW% LSS 0 exit /B 1 if %WW% GTR a exit /B 1 if %WW% GTR A exit /B 1 echo %0: %WW%x%HH%+%X%+%Y% %IMG7%magick ^ -size %WW%x%HH% xc:Black ^ -fill White -draw "point %X%,%Y%" ^ -alpha off ^ -define morphology:showkernel^=1 ^ %sSCALE% ^ -morphology convolve %qKNL% ^ %sPOST% ^ %OUTFILE% call echoRestore @endlocal & set k2iOUTFILE=%OUTFILE%
rem Given %1, an image with RGBA channels, rem creates four kernel strings, rem writing to environment variable prefix %2, suffixed _R, _G, _B and _A. rem rem CAUTION: Do not use this with large kernels, eg > 100 pixels. @rem @rem Updated: @rem 1-August-2022 for IM v7. @rem @if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion @call echoOffSave call %PICTBAT%setInOut %1 i2k set N=0 set sR= for /F "usebackq" %%L in (`%IM7DEV%magick ^ %INFILE% ^ -channel RGBA ^ -separate ^ +channel ^ -process img2knl ^ NULL:`) do ( if !N!==0 set sR=%%L if !N!==1 set sG=%%L if !N!==2 set sB=%%L if !N!==3 set sA=%%L set /A N+=1 ) if "%sR%"=="" exit /B 1 call echoRestore endlocal & set i2kOUTFILE=%OUTFILE%& set %2_R=%sR%& set %2_G=%sG%& set %2_B=%sB%& set %2_A=%sA%
rem Given %1, an image with RGBA channels, rem creates four kernel strings, rem writing to text files prefix %2, suffixed _R, _G, _B and _A. rem %3 is list of channels to extract, any of RGBA any case. rem %4 is string to insert before the colon. rem rem FIXME: When "-process img2knl" can insert text before the colon, use that instead of chStrs. @rem @rem Updated: @rem 1-August-2022 for IM v7. Assumes magick is HDRI. @rem @if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion @call echoOffSave call %PICTBAT%setInOut %1 i2kf set OUTPREF=%~dpn2 set OUTEXT=%~x2 set CHAN=%3 if "%CHAN%"=="." set CHAN= if "%CHAN%"=="" set CHAN=RGBA set BEF_COL=%~4 if "%BEF_COL%"=="." set BEF_COL= call %PICTBAT%getrgba %CHAN% if %rgbaR%==1 ( %IM7DEV%magick ^ %INFILE% ^ -channel R ^ -separate ^ +channel ^ -process img2knl ^ NULL: >%OUTPREF%_R%OUTEXT% if not "%BEF_COL%"=="" chStrs /p0 /m1 /i%OUTPREF%_R%OUTEXT% /f":" /t%BEF_COL%: ) if %rgbaG%==1 ( %IM7DEV%magick ^ %INFILE% ^ -channel G ^ -separate ^ +channel ^ -process img2knl ^ NULL: >%OUTPREF%_G%OUTEXT% if not "%BEF_COL%"=="" chStrs /p0 /m1 /i%OUTPREF%_G%OUTEXT% /f":" /t%BEF_COL%: ) if %rgbaB%==1 ( %IM7DEV%magick ^ %INFILE% ^ -channel B ^ -separate ^ +channel ^ -process img2knl ^ NULL: >%OUTPREF%_B%OUTEXT% if not "%BEF_COL%"=="" chStrs /p0 /m1 /i%OUTPREF%_B%OUTEXT% /f":" /t%BEF_COL%: ) if %rgbaA%==1 ( %IM7DEV%magick ^ %INFILE% ^ -channel A ^ -separate ^ +channel ^ -process img2knl ^ NULL: >%OUTPREF%_A%OUTEXT% if not "%BEF_COL%"=="" chStrs /p0 /m1 /i%OUTPREF%_A%OUTEXT% /f":" /t%BEF_COL%: ) dir %OUTPREF%* call echoRestore @endlocal
set %2_WW= for /F "usebackq" %%L in (`%IM%identify ^ -format "%2_WW=%%w\n%2_HH=%%h\n%2_Wm1=%%[fx:w-1]\n%2_Hm1=%%[fx:h-1]" ^ %1`) do set %%L
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)
Source file for this web page is convolve.h1. To re-create this web page, run "procH1 convolve".
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 25-September-2016.
Page created 22-Aug-2022 00:41:20.
Copyright © 2022 Alan Gibson.