Cycling colours with different methods gives results that satisfy certain criteria.
For cyclic animations of images of fractal noise, or images that have been distorted by fractal noise (see Fractal noise animations), I needed an ImageMagick process that would take a noise image and a percentage. At slightly different percentages it would create slightly different noise images, but 100% would give the same result as 0% (making it cyclic).
For convenience, this page considers just one channel, so the examples are grayscale. The same methods can be applied to three channels independently.
We have a grayscale image with values that are distributed evenly from black to white, and the mean is 50%. All values are equally likely. We want to add a value X% (0 <= X <= 100) to every pixel, to create a new image. How can we do this? There is an obvious method: "-evaluate AddModulus" does the job. This is quick and easy. If we add a small value, such as 1%, most pixels will change a small amount. Unfortunately, some pixels will change a large amount; pixels that were slightly beneath white will become slightly above black. This may be undesirable.
So we would like a number of criteria to be satisfied.
Other criteria may be desirable, for example that the standard deviation should be constant.
I don't think any method satisfies all criteria. In particular, I don't think an animated technique can have no sudden changes while also being reversible.
(For some purposes, strict reversibility is not important. Provided we can find some input that gives the desired output, we don't care if other inputs can also give that same output.)
Create a simple image that will be animated by colour-cycling, using each method.
del ccy_*.miff set SRC=ccy_src.png %IMG7%magick ^ -seed 1234 ^ -size 200x200 ^ ( xc: +noise Random ^ -channel R -separate +channel ) ^ gradient: ^ -append +repage ^ %SRC% |
For each method, we can create an animation. A loop is run with %%i as the loop counter. Environment variable LZ is set to this value with six digits and leading zeros. (I always use six digits for frame filenames, although six is overkill here.) magick creates each frame file. When the loop is finished, another magick reads all the frame files and creates an animated GIF, which is shown.
The top half of the animation shows what the method does to a noise image. The bottom half shows what the method does to a gradient image, which helps to explain the process.
Each method also calls the script ccy_stats.bat, which calculates some statistics for that method.
The statistics could be collected for every pair of adjacent frames.
Commands here use Windows cmd arithmetic, which assumes all numbers are integers. For real use, we should use floating-point arithmetic.
We will generate frames from 0 to 99, but skip even-numbered frames to save time and space.
set FOR_ARGS=0,2,99
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! %IMG7%magick ^ %SRC% ^ -evaluate AddModulus %%i%% ^ ccy_am_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_am_*.miff ^ ccy_am.gif call %PICTBAT%ccy_stats am 0 zeroAE=0 (0) zeroRMSE=80000 ch20=64241 (0.980255) pae1820=2575.29 (0.0392964) mae1820= mean20=0.49982 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A PC=2*%%i %IMG7%magick ^ %SRC% ^ -evaluate AddModulus !PC!%% ^ ccy_am2_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_am2_*.miff ^ ccy_am2.gif call %PICTBAT%ccy_stats am2 0 zeroAE=0 (0) zeroRMSE=80000 ch20=62930 (0.960251) pae1820=5116.73 (0.0780762) mae1820= mean20=0.498832 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! %IMG7%magick ^ %SRC% ^ -evaluate AddModulus %%i%% ^ -solarize 50%% ^ -evaluate Multiply 2 ^ ccy_amsol_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_amsol_*.miff ^ ccy_amsol.gif call %PICTBAT%ccy_stats amsol 79800 zeroAE=26738.8 (0.408008) zeroRMSE=79999 ch20=2639.96 (0.0402832) pae1820=2568.86 (0.0391982) mae1820= mean20=0.499046 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A PC=%%i set /A NEGPC=100-!PC! %IMG7%magick ^ %SRC% ^ ^( -clone 0 -evaluate AddModulus !PC!%% ^) ^ ^( -clone 1 -negate ^) ^ ^( -clone 0 -threshold !NEGPC!%% ^) ^ -delete 0 ^ -composite ^ ccy_amneg_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_amneg_*.miff ^ ccy_amneg.gif call %PICTBAT%ccy_stats amneg 0 zeroAE=0 (0) zeroRMSE=80000 ch20=1327.98 (0.0202637) pae1820=1297.5 (0.0197986) mae1820= mean20=0.659339 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A PC=%%i set /A NEGPC=100-!PC! %IMG7%magick ^ %SRC% ^ ^( -clone 0 -evaluate AddModulus !PC!%% ^) ^ ^( -clone 1 -negate ^) ^ ^( -clone 0 -threshold !NEGPC!%% ^) ^ -delete 0 ^ -composite ^ -auto-level ^ -auto-gamma ^ ccy_amnegaa_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_amnegaa_*.miff ^ ccy_amnegaa.gif call %PICTBAT%ccy_stats amnegaa 79167 zeroAE=29.3111 (0.000447259) zeroRMSE=79763 ch20=2559.96 (0.0390625) pae1820=957.202 (0.014606) mae1820= mean20=0.524423 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A PC=2*%%i set /A NEGPC=100-!PC! if /I !NEGPC! LEQ 0 ( set /A NEGPC=-!NEGPC! set /A PC=200-!PC! ) %IMG7%magick ^ %SRC% ^ ^( -clone 0 -evaluate AddModulus !PC!%% ^) ^ ^( -clone 1 -negate ^) ^ ^( -clone 0 -threshold !NEGPC!%% ^) ^ -delete 0 ^ -composite ^ ccy_amneg2_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_amneg2_*.miff ^ ccy_amneg2.gif call %PICTBAT%ccy_stats amneg2 0 zeroAE=0 (0) zeroRMSE=80000 ch20=2639.96 (0.0402832) pae1820=2567.78 (0.0391817) mae1820= mean20=0.739364 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A PC=2*%%i set /A NEGPC=100-!PC! if /I !NEGPC! LEQ 0 ( set /A NEGPC=-!NEGPC! set /A PC=200-!PC! ) %IMG7%magick ^ %SRC% ^ ^( -clone 0 -evaluate AddModulus !PC!%% ^) ^ ^( -clone 1 -negate ^) ^ ^( -clone 0 -threshold !NEGPC!%% ^) ^ -delete 0 ^ -composite ^ -auto-level ^ -auto-gamma ^ ccy_amneg2aa_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_amneg2aa_*.miff ^ ccy_amneg2aa.gif call %PICTBAT%ccy_stats amneg2aa 79167 zeroAE=29.3111 (0.000447259) zeroRMSE=79782 ch20=5839.91 (0.0891113) pae1820=3832.15 (0.0584748) mae1820= mean20=0.516788 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A PC=2*%%i set /A NEGPC=100-!PC! if /I !NEGPC! LEQ 0 ( set /A NEGPC=!NEGPC!+100 set sNEG=-negate ) else ( set sNEG= ) %IMG7%magick ^ %SRC% ^ ^( -clone 0 -evaluate AddModulus !PC!%% ^ !sNEG! ^) ^ ^( -clone 1 -negate ^) ^ ^( -clone 0 -threshold !NEGPC!%% ^) ^ -delete 0 ^ -composite ^ ccy_amneg3_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_amneg3_*.miff ^ ccy_amneg3.gif call %PICTBAT%ccy_stats amneg3 0 zeroAE=0 (0) zeroRMSE=80000 ch20=2639.96 (0.0402832) pae1820=2567.78 (0.0391817) mae1820= mean20=0.739364 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A PC=200-2*%%i set /A NEGPC=100-!PC! if /I !NEGPC! LSS 0 ( set /A NEGPC=!NEGPC!+100 set sNEG=-negate ) else ( set sNEG= ) %IMG7%magick ^ %SRC% ^ ^( -clone 0 -evaluate AddModulus !PC!%% ^ !sNEG! ^) ^ ^( -clone 1 -negate ^) ^ ^( -clone 0 -threshold !NEGPC!%% ^) ^ -delete 0 ^ -composite ^ ccy_amneg3n_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_amneg3n_*.miff ^ ccy_amneg3n.gif call %PICTBAT%ccy_stats amneg3n 200 zeroAE=3276.75 (0.05) zeroRMSE=80000 ch20=2639.96 (0.0402832) pae1820=2569.81 (0.0392128) mae1820= mean20=0.260846 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A PC=2*%%i set /A NEGPC=100-!PC! if /I !NEGPC! LEQ 0 ( set /A NEGPC=!NEGPC!+100 set sNEG=-negate ) else ( set sNEG= ) %IMG7%magick ^ %SRC% ^ ^( -clone 0 -evaluate AddModulus !PC!%% ^ !sNEG! ^) ^ ^( -clone 1 -negate ^) ^ ^( -clone 0 -threshold !NEGPC!%% ^) ^ -delete 0 ^ -composite ^ -auto-level ^ -auto-gamma ^ ccy_amneg3aa_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_amneg3aa_*.miff ^ ccy_amneg3aa.gif call %PICTBAT%ccy_stats amneg3aa 79167 zeroAE=29.3111 (0.000447259) zeroRMSE=79782 ch20=5839.91 (0.0891113) pae1820=3832.15 (0.0584748) mae1820= mean20=0.516788 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A DEG=%%i*36/10-90 %IMG7%magick ^ %SRC% ^ -function sinusoid 1,!DEG!,0.5,0.5 ^ ccy_sin_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_sin_*.miff ^ ccy_sin.gif call %PICTBAT%ccy_stats sin 79799 zeroAE=29880.3 (0.455943) zeroRMSE=79999 ch20=4591.93 (0.0700684) pae1820=2917.94 (0.0445249) mae1820= mean20=0.498773 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A DEG=%%i*36/10-90 %IMG7%magick ^ %SRC% ^ -function sinusoid 0.5,!DEG!,0.5,0.5 ^ ccy_sin2_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_sin2_*.miff ^ ccy_sin2.gif call %PICTBAT%ccy_stats sin2 79598 zeroAE=4942.03 (0.0754106) zeroRMSE=79999 ch20=4591.93 (0.0700684) pae1820=2911.31 (0.0444238) mae1820= mean20=0.801687 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A DEG=%%i*36/10-90 %IMG7%magick ^ %SRC% ^ -function sinusoid 0.5,!DEG!,0.5,0.5 ^ -auto-level ^ -auto-gamma ^ ccy_sin2aa_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_sin2aa_*.miff ^ ccy_sin2aa.gif call %PICTBAT%ccy_stats sin2aa 79599 zeroAE=4936.99 (0.0753336) zeroRMSE=79748 ch20=9759.85 (0.148926) pae1820=4411.45 (0.0673144) mae1820= mean20=0.572583 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A DEG=%%i*36/10-90 %IMG7%magick ^ %SRC% ^ -function sinusoid 1,!DEG!,0.5,0.5 ^ -function arcsin 1 ^ ccy_sinarc_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_sinarc_*.miff ^ ccy_sinarc.gif call %PICTBAT%ccy_stats sinarc 79800 zeroAE=26738.8 (0.408008) zeroRMSE=79999 ch20=2943.96 (0.0449219) pae1820=2848.1 (0.0434592) mae1820= mean20=0.499059 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A DEG=%%i*36/10-90 %IMG7%magick ^ %SRC% ^ -function sinusoid 0.5,!DEG!,0.5,0.5 ^ -function arcsin 1 ^ ccy_sinarc2_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_sinarc2_*.miff ^ ccy_sinarc2.gif call %PICTBAT%ccy_stats sinarc2 69494 zeroAE=6.98167 (0.000106533) zeroRMSE=79999 ch20=2943.96 (0.0449219) pae1820=2846.61 (0.0434365) mae1820= mean20=0.739352 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A DEG=270-%%i*36/10 %IMG7%magick ^ %SRC% ^ -function sinusoid 0.5,!DEG!,0.5,0.5 ^ -function arcsin 1 ^ ccy_sinarc2n_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_sinarc2n_*.miff ^ ccy_sinarc2n.gif call %PICTBAT%ccy_stats sinarc2n 69494 zeroAE=6.98167 (0.000106533) zeroRMSE=80000 ch20=2928.5 (0.044686) pae1820=2849.02 (0.0434732) mae1820= mean20=0.260854 |
|
Combine sinarc2 and sinarc2n,
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A DEG1=%%i*36/10-90 set /A DEG2=270-%%i*36/10 %IMG7%magick ^ %SRC% ^ ^( -clone 0 ^ -function sinusoid 0.5,!DEG1!,0.5,0.5 ^ -function arcsin 1 ^ ^) ^ ^( -clone 0 ^ -function sinusoid 0.5,!DEG2!,0.5,0.5 ^ -function arcsin 1 ^ ^) ^ ^( -clone 0 ^ -evaluate And 1 -fill White +opaque Black ^ ^) ^ -delete 0 ^ -composite ^ ccy_sinarc2o_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_sinarc2o_*.miff ^ ccy_sinarc2o.gif call %PICTBAT%ccy_stats sinarc2o 69494 zeroAE=6.98167 (0.000106533) zeroRMSE=79998 ch20=2943.96 (0.0449219) pae1820=2857.07 (0.043596) mae1820= mean20=0.50119 The changes in the noisy half are smooth,
The gradient part has only 200 values,
|
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! set /A DEG=%%i*36/10-90 %IMG7%magick ^ %SRC% ^ -function sinusoid 0.5,!DEG!,0.5,0.5 ^ -function arcsin 1 ^ -auto-level ^ -auto-gamma ^ ccy_sinarc2aa_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_sinarc2aa_*.miff ^ ccy_sinarc2aa.gif call %PICTBAT%ccy_stats sinarc2aa 79167 zeroAE=29.3111 (0.000447259) zeroRMSE=79786 ch20=6495.9 (0.0991211) pae1820=4221.02 (0.0644086) mae1820= mean20=0.516784 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! if %%i LSS 50 ( set /A PC=2*%%i ) else ( set /A PC=200-2*%%i ) set /A PC2=100-!PC! %IMG7%magick ^ %SRC% ^ +level !PC!%%,!PC2!%% ^ ccy_lev1_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_lev1_*.miff ^ ccy_lev1.gif call %PICTBAT%ccy_stats lev1 0 zeroAE=0 (0) zeroRMSE=79998 ch20=2623.96 (0.0400391) pae1820=1315.05 (0.0200664) mae1820= mean20=0.499889 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! if %%i LSS 50 ( set /A PCL=%%i set /A PCH=%%i*3 set /A PCH2=100-%%i ) else ( set /A PCL=100-%%i set /A PCH=300-%%i*3 set /A PCH2=%%i ) set /A PCL2=100-!PCL!*3 %IMG7%magick ^ %SRC% ^ ^( -clone 0 ^ +level !PCL!%%,!PCL2!%% ^ ^) ^ ^( -clone 0 ^ +level !PCH!%%,!PCH2!%% ^ ^) ^ ^( -clone 0 ^ -threshold 50%% ^ ^) ^ -delete 0 ^ -composite ^ ccy_lev2_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_lev2_*.miff ^ ccy_lev2.gif call %PICTBAT%ccy_stats lev2 0 zeroAE=0 (0) zeroRMSE=79999 ch20=1311.98 (0.0200195) pae1820=656.346 (0.0100152) mae1820= mean20=0.500259 |
|
for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! if %%i LSS 50 ( set /A PCL=%%i*2 set /A PCL2=100-%%i*4 set /A PCH=%%i*4 set /A PCH2=100-%%i*2 ) else ( set /A PCL=200-%%i*2 set /A PCL2=%%i*4-300 set /A PCH=400-%%i*4 set /A PCH2=%%i*2-100 ) %IMG7%magick ^ %SRC% ^ ^( -clone 0 ^ +level !PCL!%%,!PCL2!%% ^ ^) ^ ^( -clone 0 ^ +level !PCH!%%,!PCH2!%% ^ ^) ^ ^( -clone 0 ^ -threshold 50%% ^ ^) ^ -delete 0 ^ -composite ^ -auto-level ^ ccy_lev3_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ ccy_lev3_*.miff ^ ccy_lev3.gif call %PICTBAT%ccy_stats lev3 0 zeroAE=0 (0) zeroRMSE=79999 ch20=9119.86 (0.13916) pae1820=4566.01 (0.0696728) mae1820= mean20=0.501216 |
Some animations have a single frame with black in the bottom row when it should be white. This arises because "-threshold N%" turns only values above N% to white, so "-threshold 0%" turns everything white, except black which remains black. A fix is to subtract a small value from N. This is messy is a BAT script, so isn't fixed the simple demonstrations above. However, it is fixed in colCyclStr.bat.
How well do the methods satisfy the criteria?
The null method is not shown above, but is provided in the script colCyclStr.bat; it returns an empty string so there is no colour change.
Method | Criteria | |||||
---|---|---|---|---|---|---|
No change
at zero |
Non-zero
changes all |
No sudden
changes |
Pixels change
equally |
Mean 50%
stays 50% |
Reversible | |
null | pass | fail | pass (no changes at all) | pass | ||
am | pass | pass | fail | pass | pass | pass |
am2 | pass | pass | fail | pass | pass | pass |
amsol | fail | pass | pass | pass | pass | fail |
amneg | pass | pass | fail | pass | fail | fail |
amnegaa | pass | pass | fail | pass | pass | fail |
amneg2 | pass | pass | pass | pass | fail | fail |
amneg2aa | pass | pass | pass | pass | pass | fail |
amneg3 | pass | pass | pass | pass | fail | fail |
amneg3n | pass | pass | pass | pass | fail | fail |
amneg3aa | pass | pass | pass | pass | pass | fail |
sin | fail | pass | pass | fail | pass | fail |
sin2 | fail | pass | pass | pass | fail | fail |
sin2aa | fail | pass | pass | pass | slight fail | fail |
sinarc | fail | pass | pass | pass | pass | fail |
sinarc2 | pass | pass | pass | pass | fail | fail |
sinarc2n | pass | pass | pass | pass | fail | fail |
sinarc2o | pass | pass | pass | pass | pass | fail |
sinarc2aa | pass | pass | pass | pass | pass | fail |
lev1 | pass | pass | pass | fail | pass | pass |
lev2 | pass | pass | pass | fail | pass | pass |
lev2 | pass | pass | pass | fail | pass | pass |
Where a method fails the "mean 50% stays 50%" test and the image cycles through dark and light, I provide an "aa" version of the same method. This adds "-auto-level -auto-gamma" to the method, which tends to keep the mean at 50% but sometimes gives a visual appearance of accelerating and decelerating the change of colour.
The "n" suffix denotes a reversal of the method without the "n". I have provided just two of these: amneg3n and sinarc2n.
Each method has strengths and weaknesses. Leaving aside the "Reversible" criteria (because I don't think it is important), methods amneg3aa, sinarc2aa and sinarc2o pass all the tests. However, the "aa" versions suffer the acceleration/deceleration appearance, and sinarc2o relies on the least significant bit being noisy. When the LSB is not noisy, the result has artefacts.
Method sinarc2o uses the least significant bit of the input image as a mask to choose one of two transformations (sinarc2 and sinarc2n) that both have "no change at zero" but have opposite mean-shifts. The resulting mean is kept constant, without the problems introduced by -auto-level -auto-gamma.
amsol and sinarc are both useful but fail the "no change at zero" test.
The methods that have "sudden changes" may have little practical use.
The goal is for uniformally distributed noise to be handled well, and to remain uniform. IM's +noise Random creates such noise. Other noise types are not uniform and cycling changes the distribution, causing visible pulsing at each cycle. See script nseCycl.bat (results not shown here). For non-uniform noise, method sinarc2o may be the most acceptable, but a better solution is to use +noise Random, apply a chosen method, then apply a clut that changes the distribution from uniform to Gaussian or whatever is desired (perhaps using the method shown in Process modules: matching histograms).
If one of the methods is to be used, the appropriate code can be copied. If a choice of methods is to be provided, the script colCyclStr.bat can be called with a method name and percentage (which need not be an integer). It returns appropriate IM code in the variable ccsImStr, and this value can be used within a magick command.
For example:
call %PICTBAT%colCyclStr amneg 25.6 echo %ccsImStr%
( -clone 0 -evaluate AddModulus 25.6%% ) ( -clone 1 -negate ) ( -clone 0 -threshold 74.4%% ) -delete 0 -compose Over -composite
To get a list of method names available from colCyclStr, call the script colCyclList.bat:
call %PICTBAT%colCyclList
null am am2 amsol amneg amnegaa amneg2 amneg2aa amneg3 amneg3n amneg3aa sin sin2 sin2aa sinarc sinarc2 sinarc2n sinarc2o sinarc2aa lev1 lev2 lev3
The script colCyclTst.bat uses the above scripts to make a GIF animation from each method of a given input image. The input image can be colour or grayscale. To keep the GIFs small, I use the built-in "rose:".
call %PICTBAT%colCyclTst rose: call procH1 cct_gifs
The script also builds HTML code for a web page that shows each GIF: Colour cycle tests.
Each animation above shows a 2D (two-dimensional) image, transformed by a method with a different percentage to each frame. Instead, we can make a 1D image, transform it into other 1D images, and concatenate (append) them in the other dimension.
Make a 1D gradient. set SRC1D=ccy_1d_src.png %IMG7%magick ^ -size 1x256 gradient: ^ %SRC1D% |
|
Show the sinarc2aa method. echo off for /L %%i in (0,1,255) do ( set LZ=000000%%i set LZ=!LZ:~-6! for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "PC=%%[fx:100*%%i/256]" ^ xc:`) do set %%L call %PICTBAT%colCyclStr sinarc2aa !PC! %IMG7%magick ^ %SRC1D% ^ !ccsImStr! ^ ccy_1dsa_!LZ!.miff ) echo on %IMG7%magick ^ ccy_1dsa_*.miff ^ +append +repage ^ -pointsize 30 -gravity South ^ label:sinarc2aa ^ -append +repage ^ ccy_1dsa.png |
In the output image, the y-axis represents the input lightness and the x-axis represents the percentage. Each row shows the changes a pixel will undergo; for example, a pixel that is initially 25% will follow the changes shown in the row that is 25% of the way up an image. Each column shows the output values a grayscale gradient has for a given percentage.
We can put this is a loop, to make an output for each method.
set HTM=ccy_1dsa.htm del %HTM% call %PICTBAT%colCyclList >ccyList.lis echo off for /F %%M in (ccyList.lis) do ( set METH=%%M echo method=!METH! for /L %%i in (0,1,255) do ( set LZ=000000%%i set LZ=!LZ:~-6! for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "PC=%%[fx:100*%%i/256]" ^ xc:`) do set %%L call %PICTBAT%colCyclStr !METH! !PC! %IMG7%magick ^ %SRC1D% ^ !ccsImStr! ^ ccy_1dsa_!LZ!.miff ) %IMG7%magick ^ ccy_1dsa_*.miff ^ +append +repage ^ -pointsize 30 -gravity South ^ label:!METH! ^ -append +repage ^ ccy_1dsa_!METH!.png echo ^<img src="ccy_1dsa_!METH!.png" /^> >>%HTM% ) echo on
Each result, except amneg and amnegaa, can tile horizontally.
The output of one method can be used as the input to the same or a different method. Some methods double frequencies, in that a black-white gradient is converted to a double black-white-black gradient. When two doublers are concatenated, we get a quadrupling of frequencies: black-white-black-white-black.
With concatenation, the two (or more) calls to colCyclStr.bat may have different percentages, perhaps changing at different speeds.
A method could use cluts. For 0 to 50%, interpolate between identity clut (which is a gradient) and given clut. Then interpolate back to identity. We could have two cluts: interpolate from identity to first, then first to second, finally second to identity. Probably wise if caller also supplies the zero-frame clut.
The mask technique used in sinarc2o could be useful with other methods. Instead of the least significant bit, a different bit (or perhaps bit parity, or a noise image) might be used.
What method is like amneg2 but instead of the light band moving down then up, the dark band moves up then down?
A method could use a rolling clut, exactly the same size as there are frames in a cycle. Or, instead of rolling, use SRT with "-virtual-pixel tile".
:end rem del ccy_*.miff
For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.
@rem @rem Updated: @rem 22-August-2022 Upgraded for IM v7. @rem set METH=%1 set LIS=ccy_stats_%METH%.lis %IMG7%magick compare -metric AE -format "\nzeroAE=" ccy_%METH%_000000.miff %SRC% NULL: >%LIS% 2>&1 %IMG7%magick compare -metric RMSE -format "\nzeroRMSE=" ccy_%METH%_000000.miff %SRC% NULL: >>%LIS% 2>&1 %IMG7%magick compare -metric AE -format "\nch20=" ccy_%METH%_000000.miff ccy_%METH%_000020.miff NULL: >>%LIS% 2>&1 %IMG7%magick compare -metric PAE -format "\npae1820=" ccy_%METH%_000018.miff ccy_%METH%_000020.miff NULL: >>%LIS% 2>&1 %IMG7%magick compare -metric MAE -format "\nmae1820=" ccy_%METH%_000018.miff ccy_%METH%_000020.miff NULL: >>%LIS% 2>&1 %IMG7%magick ccy_%METH%_000020.miff -format "\nmean20=%%[fx:mean]" info: >>%LIS% type %LIS%
rem Given %1 is a color cycling method and %2 is a percentage of cycle, rem returns ccsImStr, a string to implement it. @rem @rem Updated: @rem 22-August-2022 Upgraded for IM v7. @rem if "%1%"=="" exit /B 1 if "%2%"=="" exit /B 1 goto %1 :null set ccsImStr= @exit /B 0 :am set ccsImStr=-evaluate AddModulus %2%%%% @exit /B 0 :am2 for /F "usebackq" %%L in (`%IMG7%magick identify -format "ccsPC=%%[fx:2*(%2)]" xc:`) do set %%L set ccsImStr=-evaluate AddModulus %ccsPC%%%%% @exit /B 0 :amsol set ccsImStr=-evaluate AddModulus %2%%%% -solarize 50%% -evaluate Multiply 2 @exit /B 0 :amneg set ccsPC=%2 for /F "usebackq" %%L in (`%IMG7%magick identify -format "ccsNEGPC=%%[fx:100-(%2)]" xc:`) do set %%L set ccsImStr=^( -clone 0 -evaluate AddModulus !ccsPC!%%%% ^) ^ ^^^( -clone 1 -negate ^^^) ^ ^^^( -clone 0 -threshold !ccsNEGPC!%%%% ^^^) ^ -delete 0 -compose Over -composite @exit /B 0 :amnegaa set ccsPC=%2 for /F "usebackq" %%L in (`%IMG7%magick identify -format "ccsNEGPC=%%[fx:100-(%2)]" xc:`) do set %%L set ccsImStr=^( -clone 0 -evaluate AddModulus !ccsPC!%%%% ^) ^ ^^^( -clone 1 -negate ^^^) ^ ^^^( -clone 0 -threshold !ccsNEGPC!%%%% ^^^) ^ -delete 0 -compose Over -composite ^ -auto-level -auto-gamma @exit /B 0 :amneg2 for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsPC=%%[fx:%2<50?2*(%2):200-2*(%2)]\nccsNEGPC=%%[fx:(%2<50?100-2*(%2):2*(%2)-100)-0.00001]" ^ xc:`) do set %%L set ccsImStr=^( -clone 0 -evaluate AddModulus !ccsPC!%%%% ^) ^ ^^^( -clone 1 -negate ^^^) ^ ^^^( -clone 0 -threshold !ccsNEGPC!%%%% ^^^) ^ -delete 0 -compose Over -composite @exit /B 0 :amneg2aa for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsPC=%%[fx:%2<50?2*(%2):200-2*(%2)]\nccsNEGPC=%%[fx:(%2<50?100-2*(%2):2*(%2)-100)-0.00001]" ^ xc:`) do set %%L set ccsImStr=^( -clone 0 -evaluate AddModulus !ccsPC!%%%% ^) ^ ^^^( -clone 1 -negate ^^^) ^ ^^^( -clone 0 -threshold !ccsNEGPC!%%%% ^^^) ^ -delete 0 -compose Over -composite ^ -auto-level -auto-gamma @exit /B 0 :amneg3 for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsPC=%%[fx:2*(%2)]\nccsNEGPC=%%[fx:%2<50?100-2*(%2):200-2*(%2)]\nccsIsNEG=%%[fx:%2<50?0:1]" ^ xc:`) do set %%L if !ccsIsNEG!==1 (set ccsNEG=-negate) else set ccsNEG= set ccsImStr=^( -clone 0 -evaluate AddModulus !ccsPC!%%%% !ccsNEG! ^) ^ ^^^( -clone 1 -negate ^^^) ^ ^^^( -clone 0 -threshold !ccsNEGPC!%%%% ^^^) ^ -delete 0 -compose Over -composite @exit /B 0 :amneg3n for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsPC=%%[fx:200-2*(%2)]\nccsNEGPC=%%[fx:(%2>=50?2*(%2)-100:2*(%2))-0.00001]\nccsIsNEG=%%[fx:%2>=50?0:1]" ^ xc:`) do set %%L if !ccsIsNEG!==1 (set ccsNEG=-negate) else set ccsNEG= set ccsImStr=^( -clone 0 -evaluate AddModulus !ccsPC!%%%% !ccsNEG! ^) ^ ^^^( -clone 1 -negate ^^^) ^ ^^^( -clone 0 -threshold !ccsNEGPC!%%%% ^^^) ^ -delete 0 -compose Over -composite @exit /B 0 :amneg3aa for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsPC=%%[fx:2*(%2)]\nccsNEGPC=%%[fx:%2<50?100-2*(%2):200-2*(%2)]\nccsIsNEG=%%[fx:%2<50?0:1]" ^ xc:`) do set %%L if !ccsIsNEG!==1 (set ccsNEG=-negate) else set ccsNEG= set ccsImStr=^( -clone 0 -evaluate AddModulus !ccsPC!%%%% !ccsNEG! ^) ^ ^^^( -clone 1 -negate ^^^) ^ ^^^( -clone 0 -threshold !ccsNEGPC!%%%% ^^^) ^ -delete 0 -compose Over -composite ^ -auto-level -auto-gamma @exit /B 0 :sin for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsDEG=%%[fx:(%2)*3.6-90]" ^ xc:`) do set %%L set ccsImStr=-function sinusoid 1,!ccsDEG!,0.5,0.5 @exit /B 0 :sin2 for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsDEG=%%[fx:(%2)*3.6-90]" ^ xc:`) do set %%L set ccsImStr=-function sinusoid 0.5,!ccsDEG!,0.5,0.5 @exit /B 0 :sin2aa for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsDEG=%%[fx:(%2)*3.6-90]" ^ xc:`) do set %%L set ccsImStr=-function sinusoid 0.5,!ccsDEG!,0.5,0.5 -auto-level -auto-gamma @exit /B 0 :sinarc for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsDEG=%%[fx:(%2)*3.6-90]" ^ xc:`) do set %%L set ccsImStr=-function sinusoid 1,!ccsDEG!,0.5,0.5 -function arcsin 1 @exit /B 0 :sinarc2 for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsDEG=%%[fx:(%2)*3.6-90]" ^ xc:`) do set %%L set ccsImStr=-function sinusoid 0.5,!ccsDEG!,0.5,0.5 -function arcsin 1 @exit /B 0 :sinarc2n for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsDEG=%%[fx:270-(%2)*3.6]" ^ xc:`) do set %%L set ccsImStr=-function sinusoid 0.5,!ccsDEG!,0.5,0.5 -function arcsin 1 @exit /B 0 :sinarc2o for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsDEG1=%%[fx:%2*3.6-90]\nccsDEG2=%%[fx:270-(%2)*3.6]" ^ xc:`) do set %%L set ccsImStr=^ ^^^( -clone 0 ^ -function sinusoid 0.5,!ccsDEG1!,0.5,0.5 ^ -function arcsin 1 ^ ^^^) ^ ^^^( -clone 0 ^ -function sinusoid 0.5,!ccsDEG2!,0.5,0.5 ^ -function arcsin 1 ^ ^^^) ^ ^^^( -clone 0 ^ -evaluate And 1 -fill White +opaque Black ^ ^^^) ^ -delete 0 ^ -compose Over -composite @exit /B 0 :sinarc2aa for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsDEG=%%[fx:(%2)*3.6-90]" ^ xc:`) do set %%L set ccsImStr=-function sinusoid 0.5,!ccsDEG!,0.5,0.5 -function arcsin 1 -auto-level -auto-gamma @exit /B 0 :lev1 for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "ccsPC=%%[fx:%2<50?2*(%2):200-2*(%2)]\nccsPC2=%%[fx:%2<50?100-2*(%2):2*(%2)-100]\n" ^ xc:`) do set %%L set ccsImStr=+level !ccsPC!%%,!ccsPC2!%% @exit /B 0 :lev2 set ccsFORM=^ ccsPCL=%%[fx:%2^<50?(%2):100-(%2)]\n^ ccsPCH=%%[fx:%2^<50?3*(%2):300-3*(%2)]\n^ ccsPCH2=%%[fx:%2^<50?100-(%2):(%2)]\n^ ccsPCL2=%%[fx:%2^<50?100-3*(%2):3*(%2)-200] for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "!ccsFORM!" ^ xc:`) do set %%L set ccsImStr=^( -clone 0 ^ +level !ccsPCL!%%%%,!ccsPCL2!%%%% ^) ^ ^^^( -clone 0 +level !ccsPCH!%%%%,!ccsPCH2!%%%% ^^^) ^ ^^^( -clone 0 -threshold 50%%%% ^^^) ^ -delete 0 -compose Over -composite @exit /B 0 :lev3 set ccsFORM=^ ccsPCL=%%[fx:%2^<50?2*(%2):200-2*(%2)]\n^ ccsPCL2=%%[fx:%2^<50?100-4*(%2):4*(%2)-300]\n^ ccsPCH=%%[fx:%2^<50?4*(%2):400-4*(%2)]\n^ ccsPCH2=%%[fx:%2^<50?100-2*(%2):2*(%2)-100] for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "!ccsFORM!" ^ xc:`) do set %%L set ccsImStr=^( -clone 0 ^ +level !ccsPCL!%%%%,!ccsPCL2!%%%% ^) ^ ^^^( -clone 0 +level !ccsPCH!%%%%,!ccsPCH2!%%%% ^^^) ^ ^^^( -clone 0 -threshold 50%%%% ^^^) ^ -delete 0 ^ -compose Over -composite -auto-level @exit /B 0
I don't provide the source or binary of chStrs.exe, which here simply strips any colons (":") from the piped stream.
@rem Write a list of available colour cycle methods to stdout @findstr "^:" %PICTBAT%colCyclStr.bat |chStrs /p0 /i- /o- /f":"
rem Given image %1, rem for each colour cycling method, makes test GIF named cct_!METH!.gif rem Also creates an HTM file, name %2. rem %3 optional prefix for GIF files. [cct] @rem @rem Updated: @rem 25-September-2022 for IM v7. @rem @if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal @call echoOffSave call %PICTBAT%setInOut %1 pd set HTM=%2 if "%HTM%"=="" set HTM=cct_gifs.h1 set GIF_PREF=%3 if "%GIF_PREF%"=="" set GIF_PREF=cct set TMPDIR=%TEMP%\ del %TMPDIR%cct_*.miff 2>nul set METH_LIST=%TEMP%\cctList.lis del %HTM% 2>nul set TXT=%TMPDIR%cct_text.png for /F "usebackq" %%L in (`%IMG7%magick identify ^ -format "WW=%%w\nHH=%%h\nWT=%%[fx:max(w*0.9,50)]\nHT=%%[fx:max(h*0.9,20)]" ^ %INFILE%`) do set %%L echo ^<h1^>Color cycle tests^</h1^> >%HTM% echo ^<p^>This page is automatically created by the script ^<tt^>colCyclTst.bat^</tt^>. >>%HTM% echo See ^<a href="colcycl.htm#cct"^>Colour cycling: tests^</a^>.^</p^> >>%HTM% set FOR_ARGS=0,2,99 call %PICTBAT%colCyclList >%METH_LIST% for /F %%M in (%METH_LIST%) do ( set METH=%%M echo method=!METH! %IMG7%magick ^ -size %WT%x%HT% label:!METH! ^ -trim +repage ^ -bordercolor White -border 3 ^ -size %WW%x%HH% xc: ^ +swap ^ -gravity South -append +repage ^ %TXT% for /L %%i in (%FOR_ARGS%) do ( set LZ=000000%%i set LZ=!LZ:~-6! call %PICTBAT%colCyclStr !METH! %%i %IMG7%magick ^ %INFILE% ^ !ccsImStr! ^ %TMPDIR%cct_!LZ!.miff ) %IMG7%magick ^ -loop 0 ^ %TXT% ^ null: ^ %TMPDIR%cct_*.miff ^ -gravity North ^ -layers composite ^ +repage ^ -layers optimize ^ %GIF_PREF%_!METH!.gif del %TMPDIR%cct_*.miff echo ^<img src="%GIF_PREF%_!METH!.gif" /^> >>%HTM% ) setlocal DisableDelayedExpansion echo ^<!--Page-version v1.0 23-Sep-2015--^> >>%HTM% endlocal echo Created cct_*.gif and %HTM% call echoRestore @endlocal & set pdOUTFILE=%HTM%
This creates a single web page showing all colour cycling methods for all +noise types.
@rem @rem Updated: @rem 22-August-2022 Upgraded for IM v7. @rem set OUT_HTM=nc_htm.htm del %OUT_HTM% for /F "usebackq" %%N in (`%IMG7%magick -list noise`) do ( echo %%N %IMG7%magick ^ -size 200x200 xc:gray^(50%%^) +noise %%N ^ nc_%%N.png set HTM=htm_%%N call %PICTBAT%colCyclTst nc_%%N.png !HTM! nc_%%N echo ^<h2^>%%N^</h2^> >>%OUT_HTM% type !HTM! >>%OUT_HTM% )
All images on this page were created by the commands shown, using:
%IMG7%magick -version
Version: ImageMagick 6.9.1--6 Q16 x64 2015-06-20 http://www.imagemagick.org Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC License: http://www.imagemagick.org/script/license.php Features: Cipher DPC Modules OpenMP Delegates (built-in): bzlib cairo freetype jng jp2 jpeg lcms lqr openexr pangocairo png ps rsvg tiff webp xml zlib
To improve internet download speeds, some images may have been automatically converted (by ImageMagick, of course) from PNG or TIFF or MIFF to JPG.
Source file for this web page is colcycl.h1. To re-create this web page, run "procH1 colcycl".
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 18-September-2015.
Page created 25-Sep-2022 03:42:28.
Copyright © 2022 Alan Gibson.