A wobbly line can be simplified to a straight line using linear regression.
A graphic element may be roughly a straight line, and we want to replace it by an exactly straight line that is a "best" approximation. One technique for this is linear regression.
See Wikipedia: Simple linear regression.
The input is a white line on a black background.
rln_clut1.png |
From the input image, we make a clut:
Trim it, record the crop parameters, and spread the line downwards. for /F "usebackq" %%L in (`%IM%convert ^ rln_clut1.png ^ -trim ^ -format "WW=%%w\nHH=%%h\noffsX=%%X\noffsY=%%Y" ^ +write info: ^ +repage ^ -morphology dilate:-1 rectangle:1x2+0+0 ^ lr_clut2.png`) do set %%L echo WW=%WW% HH=%HH% offsX=%offsX% offsY=%offsY% WW=600 HH=97 offsX=+0 offsY=+150 |
|
A clut image is usually one pixel high.
%IM%convert ^ lr_clut2.png ^ -scale "x1^!" ^ +write lr_clut3.png ^ -scale "x20^!" ^ lr_clut3a.png set CLUT_SRC=lr_clut3.png |
|
From the clut, we can reconstruct an approximation to the line, though this creates a non-aliased version.
%IM%convert ^ lr_clut3.png ^ -scale "x%HH%^!" ^ ( +clone ^ -sparse-color Bilinear "0,0,White 0,%%[fx:h-1],Black" ^ ) ^ -compose MinusDst -composite ^ -fill White +opaque Black ^ -negate ^ lr_clut4.png |
From the clut, we can do a linear regression. This is the grayscale linear gradient the same size as the clut that has a minimum RMS error from the clut.
nPoints * sumXY - sigX * sigY slope = ----------------------------------- nPoints * sumXsquared - sigX * sigX intercept = meanY - slope * meanX
where, taking X and Y as zero to one:
We need the mean values of two new images. We save and show the images for interest.
for /F "usebackq" %%L in (`%IM%convert ^ %CLUT_SRC% ^ ^( +clone ^ -sparse-color bilinear ^ "0,0,Black %%[fx:w-1],0,White" ^ ^) ^ -compose Multiply -composite ^ -format "meanClGr=%%[fx:mean]" ^ +write info: ^ -scale "x20^!" ^ lr_ClGr.png`) do set %%L echo meanClGr=%meanClGr% meanClGr=0.185304 |
|
for /F "usebackq" %%L in (`%IM%convert ^ %CLUT_SRC% ^ -sparse-color bilinear ^ "0,0,Black %%[fx:w-1],0,White" ^ -evaluate Pow 2 ^ -format "meanClSq=%%[fx:mean]" ^ +write info: ^ -scale "x20^!" ^ lr_ClSq.png`) do set %%L echo meanClSq=%meanClSq% meanClSq=0.333613 |
Calculate the components for the slope and intercept:
set sFORMAT1=^ nPoints=%WW%\n^ meanY=%%[fx:mean]\n^ sigX=%%[fx:0.5*%WW%]\n^ sigY=%%[fx:mean*%WW%]\n^ sumXY=%%[fx:%WW%*%meanClGr%]\n^ sumXsquared=%%[fx:%WW%*%meanClSq%] for /F "usebackq" %%L in (`%IM%identify ^ -format "%sFORMAT1%" ^ %CLUT_SRC%`) do set %%L echo nPoints=%nPoints% meanY=%meanY% sigX=%sigX% sigY=%sigY% sumXY=%sumXY% sumXsquared=%sumXsquared%
nPoints=600 meanY=0.484337 sigX=300 sigY=290.602 sumXY=111.182 sumXsquared=200.168
Calculate the slope:
set sFORMAT2=^ slope=%%[fx:(%WW%*%sumXY%-%sigX%*%sigY%)/(%WW%*%sumXsquared%-%sigX%*%sigX%)] for /F "usebackq" %%L in (`%IM%identify ^ -format "%sFORMAT2%" ^ xc:`) do set %%L echo slope=%slope%
slope=-0.680095
Calculate the intercept:
set sFORMAT3=^ intercept=%%[fx:%meanY%-%slope%*0.5] for /F "usebackq" %%L in (`%IM%identify ^ -format "%sFORMAT3%" ^ xc:`) do set %%L echo intercept=%intercept%
intercept=0.824385
From the slope and intersept, we calculate the ends of the line.
At x==0 pixels, the regression line is at HH*intercept pixels (where zero is at the bottom) or HH*(1-intercept) pixels (where zero is at the top).
At x==WW-1 pixels, the regression line is at HH*intercept+slope pixels (where zero is at the bottom) or HH*(1-intercept-slope) pixels (where zero is at the top).
set /A A.xi=0 set /A B.xi=%WW%-1 set FORMAT4=^ A.yi=%%[fx:int(%HH%*(1-(%intercept%))+0.5)]\n^ B.yi=%%[fx:int(%HH%*(1-(%intercept%)-(%slope%))+0.5)] for /F "usebackq" %%L in (`%IM%identify ^ -format "%FORMAT4%" ^ xc:`) do set %%L echo A.xi,A.yi = %A.xi%,%A.yi% B.xi,B.yi = %B.xi%,%B.yi%
A.xi,A.yi = 0,17 B.xi,B.yi = 599,83
%IM%convert ^ lr_clut2.png ^ -stroke Orange ^ -strokewidth 2 ^ -draw "line %A.xi%,%A.yi% %B.xi%,%B.yi%" ^ lr_reglin.png |
If we add the offsets from the trim, we get the coordinates for the input image so we can draw the line on the input image. or use the coordinates for futher processing.
set /A A.xi+=%offsX% set /A A.yi+=%offsY% set /A B.xi+=%offsX% set /A B.yi+=%offsY% echo A.xi,A.yi = %A.xi%,%A.yi% B.xi,B.yi = %B.xi%,%B.yi% A.xi,A.yi = 0,167 B.xi,B.yi = 599,233 %IM%convert ^ rln_clut1.png ^ -stroke Orange ^ -strokewidth 2 ^ -draw "line %A.xi%,%A.yi% %B.xi%,%B.yi%" ^ lr_reglin2.png |
We implement the above as a pair of scripts.
The script clut2LinReg.bat takes a grayscale Nx1 clut file and sets an environment variable.
call %PICTBAT%clut2LinReg lr_clut3.png LINREG_clut3 if ERRORLEVEL 1 goto :error set LINREG_clut3
LINREG_clut3.intercept=0.82438278569772261 LINREG_clut3.nPoints=600 LINREG_clut3.slope=-0.68009234334936286
%IM%convert -size 1x10000 gradient: -rotate 90 lr_g1.png call %PICTBAT%clut2LinReg lr_g1.png LINREG1 if ERRORLEVEL 1 goto :error set LINREG1
LINREG1.intercept=0 LINREG1.nPoints=10000 LINREG1.slope=1
%IM%convert ^ lr_g1.png ^ ( +clone -flop ) ^ +append ^ lr_g2.png call %PICTBAT%clut2LinReg lr_g2.png LINREG2 if ERRORLEVEL 1 goto :error set LINREG2
LINREG2.intercept=0.49999542271250075 LINREG2.nPoints=20000 LINREG2.slope=9.1545749985121921e-006
The script linReg2xy.bat calculates the coordinates of the ends of the regression line.
call %PICTBAT%linReg2xy LINREG_clut3 %WW%x%HH% %offsX% %offsY% set LINREG_clut3
LINREG_clut3.A.x=0 LINREG_clut3.A.y=167.035 LINREG_clut3.B.x=599 LINREG_clut3.B.y=233.004 LINREG_clut3.intercept=0.82438278569772261 LINREG_clut3.nPoints=600 LINREG_clut3.slope=-0.68009234334936286
Verify: when the clut is a straight line, the regression line should coincide.
Linear regression offers an alternative method for simplifying noisy quadrangles. For example, we will consider the top "edge" of is_easy_mainland.png. We assume the edge is defined by the most westerly and easterly edge of the shape, within the top 50% of the image.
is_easy_mainland.png |
|
%IM%convert ^ is_easy_mainland.png ^ -gravity North ^ -crop x50%%+0+0 ^ lr_nq1.png |
|
Trim black off, wthout removing canvas metadata. %IM%convert ^ lr_nq1.png ^ -bordercolor Black -border 1 ^ -trim ^ lr_nq2.png |
|
Record the width, height and canvas offset.
for /F "usebackq" %%L in (`%IM%identify ^ -format "lrWW=%%w\nlrHH=%%h\nlrOFFX=%%X-1\nlrOFFY=%%Y-1" ^ lr_nq2.png`) do set /A %%L echo lrWW=%lrWW% lrHH=%lrHH% lrOFFX=%lrOFFX% lrOFFY=%lrOFFY% lrWW=288 lrHH=87 lrOFFX=18 lrOFFY=13 |
[No image] |
%IM%convert ^ lr_nq2.png ^ -background Black ^ -splice 0x1+0+0 ^ -fill Red ^ -draw "color 0,0 floodfill" ^ -chop 0x1+0+0 ^ lr_nq3.png |
|
%IM%convert ^ lr_nq3.png ^ -fill White -opaque Black ^ -fill Black -opaque Red ^ lr_nq4.png |
|
%IM%convert ^ lr_nq4.png ^ -scale "x1^!" ^ +write lr_nq5.png ^ -scale "x20^!" ^ lr_nq5a.png |
|
The image lr_nq4.png is a first approximation to the "top edge". But this approximation can be misleading. Better (perhaps) to trim, then walk from each corner towards the opposite corner (or, more simply, at a 45° angle, or slide a ruler perpendicular to that angle) until we hit an edge. This gives us four points on the edge of the shape. Use these as the inner bounds for the four edges. But the centre of an edge might bend inwards further than that.
How about: rotate -90° and find first white. Rotate those coords back (sin cos cos sin) to give X,Y. X is the right-side of the top edge. Y is the top-side of the right edge.
Or: unroll, crop into four horizontally, flip, find first white in each. This gives radius and theta of corners.
From the Nx1 clut file, the script clut2LinReg.bat calculates the linear regression intercept and slope.
call %PICTBAT%clut2LinReg lr_nq5.png LINREG_nq5 if ERRORLEVEL 1 goto :error set LINREG_nq5
LINREG_nq5.intercept=1.0433506627567346 LINREG_nq5.nPoints=288 LINREG_nq5.slope=-0.2669042705046954
call %PICTBAT%linReg2xy LINREG_nq5 %lrWW%x%lrHH% %lrOFFX% %lrOFFY% set LINREG_nq5
LINREG_nq5.A.x=18 LINREG_nq5.A.y=9.22849 LINREG_nq5.B.x=305 LINREG_nq5.B.y=32.4492 LINREG_nq5.intercept=1.0433506627567346 LINREG_nq5.nPoints=288 LINREG_nq5.slope=-0.2669042705046954
The A and B coordinates give the ends of the regression line, with respect to the input image.
set sLINE=^ line ^ %LINREG_nq5.A.x%,%LINREG_nq5.A.y% ^ %LINREG_nq5.B.x%,%LINREG_nq5.B.y% %IM%convert ^ is_easy_mainland.png ^ -stroke Orange ^ -draw "%sLINE%" ^ lr_1reg.png |
We implement this as a script nqlrTop.bat, which finds the linear regression of the top edge of a noisy quadrangle.
call %PICTBAT%nqlrTop is_easy_mainland.png LINREG_top set LINREG_top
LINREG_top.A.x=18 LINREG_top.A.y=9.22849 LINREG_top.B.x=305 LINREG_top.B.y=32.4492 LINREG_top.intercept=1.0433506627567346 LINREG_top.nPoints=288 LINREG_top.slope=-0.2669042705046954
To find the other three edges, we call this three more times, rotating the input image and output numbers. The script nqlr4edges.bat does this.
call %PICTBAT%nqlr4edges ^ is_easy_mainland.png LINREG_4edges lr_4lines.png set LINREG_4edges LINREG_4edges.bottom.B.y=305 LINREG_4edges.bottomLine.A.x=301 LINREG_4edges.bottomLine.A.y=183.47129999999999 LINREG_4edges.bottomLine.B.x=14 LINREG_4edges.bottomLine.B.y=173.7936 LINREG_4edges.leftLine.A.x=53.367 LINREG_4edges.leftLine.A.y=190 LINREG_4edges.leftLine.B.x=12.4533 LINREG_4edges.leftLine.B.y=13 LINREG_4edges.rightLine.A.x=279.2056 LINREG_4edges.rightLine.A.y=13 LINREG_4edges.rightLine.B.x=286.83420000000001 LINREG_4edges.rightLine.B.y=189 LINREG_4edges.topLine.A.x=18 LINREG_4edges.topLine.A.y=9.22849 LINREG_4edges.topLine.B.x=305 LINREG_4edges.topLine.B.y=32.4492 LINREG_4edges.topLine.intercept=1.0433506627567346 LINREG_4edges.topLine.nPoints=288 LINREG_4edges.topLine.slope=-0.2669042705046954 |
The script nqlr4edges.bat doesn't calculate the rotated values of intercept and slope of the other three edges.
The script lines2Quad.bat calculates the intersection of these four lines.
call %PICTBAT%lines2Quad LINREG_4edges set LINREG_4edges.TL set LINREG_4edges.TR set LINREG_4edges.BR set LINREG_4edges.BL
LINREG_4edges.TL.liNeedExtend=1 LINREG_4edges.TL.sI=1.0242978350521272 LINREG_4edges.TL.tI=-0.02279029384659309 LINREG_4edges.TL.x=11.459185666027778 LINREG_4edges.TL.y=8.6992831957734893 LINREG_4edges.TR.liNeedExtend=0 LINREG_4edges.TR.sI=0.098996128474231448 LINREG_4edges.TR.tI=0.91275540719748605 LINREG_4edges.TR.x=279.96080186567855 LINREG_4edges.TR.y=30.423318611464737 LINREG_4edges.BR.liNeedExtend=0 LINREG_4edges.BR.sI=0.96582292397884839 LINREG_4edges.BR.tI=0.050266631505696685 LINREG_4edges.BR.x=286.57347675786508 LINREG_4edges.BR.y=182.98483462027733 LINREG_4edges.BL.liNeedExtend=0 LINREG_4edges.BL.sI=0.084722151292207545 LINREG_4edges.BL.tI=0.87491044139834162 LINREG_4edges.BL.x=49.900703318676008 LINREG_4edges.BL.y=175.00417922127926
This gives us the coordinates of the four corners of a quadrangle. TL = top-left, etc. sI and tI are measures of how far along each line is the point of intersection. liNeedExtend is whether sI or tI are outside the range 0.0 to 1.0, ie whether a line needed extending to form that intersection.
As a check, we use script drawCircs.bat to circle the calculated intersections:
call %PICTBAT%drawCircs ^ lr_4lines.png lr_4linescircs.png . . . ^ LINREG_4edges.TL LINREG_4edges.TR ^ LINREG_4edges.BL LINREG_4edges.BR |
We can also test against an exact quadrangle ...
%IM%convert ^ -size 300x200 xc:Black ^ -fill White ^ -draw "polygon 50,50 250,60 230,140 60,150" ^ lr_ex_quad.png |
|
call %PICTBAT%nqlr4edges ^ lr_ex_quad.png LINREG_quad lr_exq_out.png call %PICTBAT%lines2Quad LINREG_quad call %PICTBAT%drawCircs ^ lr_exq_out.png lr_exq_out.png . . . ^ LINREG_quad.TL LINREG_quad.TR ^ LINREG_quad.BL LINREG_quad.BR set LINREG_quad |
LINREG_quad.BL.liNeedExtend=0 LINREG_quad.BL.sI=0.011780379998937957 LINREG_quad.BL.tI=0.97223919489048283 LINREG_quad.BL.x=60.135748945260637 LINREG_quad.BL.y=148.8219620001062 LINREG_quad.bottom.B.y=244 LINREG_quad.bottomLine.A.x=240 LINREG_quad.bottomLine.A.y=136.35980000000001 LINREG_quad.bottomLine.B.x=55 LINREG_quad.bottomLine.B.y=149.17779999999999 LINREG_quad.BR.liNeedExtend=0 LINREG_quad.BR.sI=0.92256526662273886 LINREG_quad.BR.tI=0.058395126339815126 LINREG_quad.BR.x=229.1969016271342 LINREG_quad.BR.y=137.10830872942375 LINREG_quad.leftLine.A.x=60.2537 LINREG_quad.leftLine.A.y=150 LINREG_quad.leftLine.B.x=50.2412 LINREG_quad.leftLine.B.y=50 LINREG_quad.rightLine.A.x=242.37630000000001 LINREG_quad.rightLine.A.y=55 LINREG_quad.rightLine.B.x=228.0907 LINREG_quad.rightLine.B.y=144 LINREG_quad.TL.liNeedExtend=0 LINREG_quad.TL.sI=0.99772475481221745 LINREG_quad.TL.tI=0.0013199044622133581 LINREG_quad.TL.x=50.263980892442675 LINREG_quad.TL.y=50.227524518778253 LINREG_quad.topLine.A.x=50 LINREG_quad.topLine.A.y=50.2143 LINREG_quad.topLine.B.x=250 LINREG_quad.topLine.B.y=60.2336 LINREG_quad.topLine.intercept=0.9957145570156678 LINREG_quad.topLine.nPoints=201 LINREG_quad.topLine.slope=-0.20038615988469638 LINREG_quad.TR.liNeedExtend=0 LINREG_quad.TR.sI=0.054078400479930358 LINREG_quad.TR.tI=0.95801878801051965 LINREG_quad.TR.x=241.60375760210391 LINREG_quad.TR.y=59.812977642713804
... and an exact rectangle.
%IM%convert ^ -size 300x200 xc:Black ^ -fill White ^ -draw "polygon 30,40 230,40 230,140 30,140" ^ lr_ex_rect.png |
|
call %PICTBAT%nqlr4edges ^ lr_ex_rect.png LINREG_rect lr_rect_out.png call %PICTBAT%lines2Quad LINREG_rect call %PICTBAT%drawCircs ^ lr_rect_out.png lr_rect_out.png . . . ^ LINREG_rect.TL LINREG_rect.TR ^ LINREG_rect.BL LINREG_rect.BR |
A photograph of a book page. The image is about 5000x7000 pixels. All operations are done on the full-size image, and the results are resized for the web.
Source image. set WEB_SIZE=-resize 600x600 set PHOTO_SRC=pbp_threshpap.tiff %IM%convert ^ %PHOTO_SRC% ^ %WEB_SIZE% ^ lr_ph_src_sm.jpg |
Blah.
For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.
rem From clut image %1, rem sets linear regression information to environment variable, prefix %2. rem 3 channels? @if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion rem @call echoOffSave call %PICTBAT%setInOut %1 c2lr set ENV_PREF=%2 if "%ENV_PREF%"=="." set ENV_PREF= if "%ENV_PREF%"=="" set ENV_PREF=LINREG set meanClGr= set meanClSq= for /F "usebackq" %%L in (`%IM%convert ^ -precision 19 ^ %INFILE% ^ ^( +clone ^ -sparse-color bilinear ^ "0,0,Black %%[fx:w-1],0,White" ^ ^) ^ -compose Multiply -composite ^ -format "HH=%%h\nmeanClGr=%%[fx:mean]" ^ info:`) do set %%L if not "%HH%"=="1" exit /B 1 for /F "usebackq" %%L in (`%IM%convert ^ -precision 19 ^ %INFILE% ^ -sparse-color bilinear ^ "0,0,Black %%[fx:w-1],0,White" ^ -evaluate Pow 2 ^ -format "meanClSq=%%[fx:mean]" ^ info:`) do set %%L goto skipMult for /F "usebackq" %%L in (`%IM%convert ^ -precision 19 ^ %INFILE% ^ -duplicate 1 ^ -compose Multiply -composite ^ -format "meanClSq=%%[fx:mean]" ^ info:`) do set %%L :skipMult echo %0: meanClGr=%meanClGr% meanClSq=%meanClSq% set nPoints=0 set sFORMAT1=^ nPoints=%%w\n^ meanY=%%[fx:mean]\n^ sigX=%%[fx:0.5*w]\n^ sigY=%%[fx:mean*w]\n^ sumXY=%%[fx:w*%meanClGr%]\n^ sumXsquared=%%[fx:w*%meanClSq%] for /F "usebackq" %%L in (`%IM%identify ^ -precision 19 ^ -format "%sFORMAT1%" ^ %INFILE%`) do set %%L echo %0: nPoints=%nPoints% meanY=%meanY% sigX=%sigX% sigY=%sigY% sumXY=%sumXY% sumXsquared=%sumXsquared% if %nPoints%==0 exit /B 1 set sFORMAT2=^ slope=%%[fx:(%nPoints%*%sumXY%-%sigX%*%sigY%)/(%nPoints%*%sumXsquared%-%sigX%*%sigX%)] for /F "usebackq" %%L in (`%IM%identify ^ -precision 19 ^ -format "%sFORMAT2%" ^ xc:`) do set %%L set sFORMAT3=^ intercept=%%[fx:%meanY%-(%slope%)*0.5] for /F "usebackq" %%L in (`%IM%identify ^ -precision 19 ^ -format "%sFORMAT3%" ^ xc:`) do set %%L rem set ENV_LIST=c2lrEnv.lis rem set %ENV_PREF% >%ENV_LIST% call echoRestore rem endlocal & for /F %%L in (%ENV_LIST%) do @set %%L endlocal & set %ENV_PREF%.nPoints=%nPoints%& set %ENV_PREF%.slope=%slope%& set %ENV_PREF%.intercept=%intercept%
rem Given %1 is prefix for an environment variable with intercept and slope, rem %2 is width and height in form WxH rem %3 is X-offset rem %4 is Y-offset rem sets .A.x, .A.y, .B.x, .B.y @if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion rem @call echoOffSave set PREF=%1 call parseXxY2 !%PREF%.nPoints! 10 WH %2 set offsX=%3 if "%offsX%"=="." set offsX= if "%offsX%"=="" set offsX=0 set offsY=%4 if "%offsY%"=="." set offsY= if "%offsY%"=="" set offsY=0 set %PREF% set WH set /A A.x=%offsX% set /A B.x=%offsX%+%WH_X%-1 set FORMAT4=^ A.y=%%[fx:%offsY%+%WH_Y%*(1-(!%PREF%.intercept!))]\n^ B.y=%%[fx:%offsY%+%WH_Y%*(1-(!%PREF%.intercept!)-(!%PREF%.slope!))] for /F "usebackq" %%L in (`%IM%identify ^ -format "%FORMAT4%" ^ xc:`) do set %%L rem call echoRestore endlocal & set %PREF%.A.x=%A.x%& set %PREF%.A.y=%A.y%& set %PREF%.B.x=%B.x%& set %PREF%.B.y=%B.y%
rem Given %1 is a white noisy quadrangle on a black backround, rem %2 is the prefix of an environment variable, rem calculates the linear regression of the top edge. @if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1 set nqlrtINFILE=%1 set nqlrtENV_PREF=%2 set nqlrtTEMP=nqlrt_clut.miff for /F "usebackq" %%L in (`%IM%convert ^ %nqlrtINFILE% ^ -gravity North ^ -crop x50%%+0+0 ^ -threshold 50%% ^ -bordercolor Black -border 1 ^ -trim ^ -format "nqlrtWW=%%w\nnqlrtHH=%%h\nnqlrtOFFX=%%X-1\nnqlrtOFFY=%%Y-1" ^ +write info: ^ +repage ^ +write a1.png ^ -background Black ^ -splice 0x1+0+0 ^ -fill Red ^ -draw "color 0,0 floodfill" ^ -chop 0x1+0+0 ^ -fill White -opaque Black ^ -fill Black -opaque Red ^ +write a2.png ^ -scale "x1^!" ^ %nqlrtTEMP%`) do set /A %%L call %PICTBAT%clut2LinReg %nqlrtTEMP% %nqlrtENV_PREF% if ERRORLEVEL 1 exit /B 1 call %PICTBAT%linReg2xy %nqlrtENV_PREF% %nqlrtWW%x%nqlrtHH% %nqlrtOFFX% %nqlrtOFFY% if ERRORLEVEL 1 exit /B 1 exit /B 0
rem Given %1 is a white noisy quadrangle on a black backround, rem %2 is the prefix of an environment variable, rem calculates the linear regression of the four edges. rem %3 is outfile. @if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1 rem @setlocal enabledelayedexpansion rem @call echoOffSave @rem This assumes "enabledelayedexpansion" is in effect. set TESTDEL=abc if not !TESTDEL!==abc exit /B 1 set nqlr4INFILE=%1 set nqlr4ENV_PREF=%2 set nqlr4OUTFILE=%3 if "%nqlr4OUTFILE%"=="." set nqlr4OUTFILE= if "%nqlr4OUTFILE%"=="" set nqlr4OUTFILE=NULL: set EXT=.miff set TMP_ROT=nqlr4%EXT% set nqlr4WW= for /f "usebackq" %%L in (`%IM%convert ^ %nqlr4INFILE% ^ -format "nqlr4WW=%%w\nnqlr4HH=%%h" ^ +write info: ^ -rotate 90 +repage ^ %TMP_ROT%`) do set %%L if "%nqlr4WW%"=="" exit /B 1 echo nqlr4WW=%nqlr4WW% nqlr4HH=%nqlr4HH% :: Do top edge. call %PICTBAT%nqlrTop %nqlr4INFILE% %nqlr4ENV_PREF%.topLine if ERRORLEVEL 1 exit /B 1 :: Do left edge. call %PICTBAT%nqlrTop %TMP_ROT% nqlr4 if ERRORLEVEL 1 exit /B 1 set %nqlr4ENV_PREF%.leftLine.A.x=%nqlr4.A.y% call :doCalc %nqlr4ENV_PREF%.leftLine.A.y "%nqlr4HH%-1-%nqlr4.A.x%" set %nqlr4ENV_PREF%.leftLine.B.x=%nqlr4.B.y% call :doCalc %nqlr4ENV_PREF%.leftLine.B.y "%nqlr4HH%-1-%nqlr4.B.x%" :: Do right edge. %IM%convert ^ %nqlr4INFILE% ^ -rotate -90 +repage ^ %TMP_ROT% call %PICTBAT%nqlrTop %TMP_ROT% nqlr4 if ERRORLEVEL 1 exit /B 1 set %nqlr4ENV_PREF%.rightLine.A.y=%nqlr4.A.x% call :doCalc %nqlr4ENV_PREF%.rightLine.A.x "%nqlr4WW%-1-%nqlr4.A.y%" set %nqlr4ENV_PREF%.rightLine.B.y=%nqlr4.B.x% call :doCalc %nqlr4ENV_PREF%.rightLine.B.x "%nqlr4WW%-1-%nqlr4.B.y%" :: Do bottom edge. %IM%convert ^ %nqlr4INFILE% ^ -rotate 180 +repage ^ %TMP_ROT% call %PICTBAT%nqlrTop %TMP_ROT% nqlr4 if ERRORLEVEL 1 exit /B 1 call :doCalc %nqlr4ENV_PREF%.bottomLine.A.x "%nqlr4WW%-1-%nqlr4.A.x%" call :doCalc %nqlr4ENV_PREF%.bottomLine.A.y "%nqlr4HH%-1-%nqlr4.A.y%" set %nqlr4ENV_PREF%.bottom.B.y=%nqlr4.B.x% call :doCalc %nqlr4ENV_PREF%.bottomLine.B.x "%nqlr4WW%-1-%nqlr4.B.x%" call :doCalc %nqlr4ENV_PREF%.bottomLine.B.y "%nqlr4HH%-1-%nqlr4.B.y%" set sLINES=^ line ^ !%nqlr4ENV_PREF%.leftLine.A.x!,!%nqlr4ENV_PREF%.leftLine.A.y!,^ !%nqlr4ENV_PREF%.leftLine.B.x!,!%nqlr4ENV_PREF%.leftLine.B.y! ^ line ^ !%nqlr4ENV_PREF%.topLine.A.x!,!%nqlr4ENV_PREF%.topLine.A.y!,^ !%nqlr4ENV_PREF%.topLine.B.x!,!%nqlr4ENV_PREF%.topLine.B.y! ^ line ^ !%nqlr4ENV_PREF%.rightLine.A.x!,!%nqlr4ENV_PREF%.rightLine.A.y!,^ !%nqlr4ENV_PREF%.rightLine.B.x!,!%nqlr4ENV_PREF%.rightLine.B.y! ^ line ^ !%nqlr4ENV_PREF%.bottomLine.A.x!,!%nqlr4ENV_PREF%.bottomLine.A.y!,^ !%nqlr4ENV_PREF%.bottomLine.B.x!,!%nqlr4ENV_PREF%.bottomLine.B.y! if not "%nqlrOUTFILE%"=="NULL:" ( %IM%convert ^ %nqlr4INFILE% ^ -stroke Orange ^ -draw "%sLINES%" ^ %nqlr4OUTFILE% ) exit /B 0 ::---------------------------------------------------- :: Subroutine :doCalc set %1= for /F "usebackq" %%L in (`%IM%identify -precision 19 -format "%1=%%[fx:%~2]" xc:`) do set %%L rem echo %1 is !%1! if "!%1!"=="" exit /B 1 exit /B 0
rem Given %1 is environment variable prefix rem with leftLine, topLine, rightLine and BottomLine rem each with A and B rem each with x and y, rem calculates intersections. rem %2 is badness threshold for intersection. set ENV_PREF=%1 set l2qBAD_THRESH=%2 if "%l2qBAD_THRESH%"=="" set l2qBAD_THRESH=. call %PICTBAT%lineIntersect %ENV_PREF%.leftLine %ENV_PREF%.topLine %ENV_PREF%.TL %l2qBAD_THRESH% if ERRORLEVEL 1 exit /B 1 call %PICTBAT%lineIntersect %ENV_PREF%.rightLine %ENV_PREF%.topLine %ENV_PREF%.TR %l2qBAD_THRESH% if ERRORLEVEL 1 exit /B 1 call %PICTBAT%lineIntersect %ENV_PREF%.leftLine %ENV_PREF%.bottomLine %ENV_PREF%.BL %l2qBAD_THRESH% if ERRORLEVEL 1 exit /B 1 call %PICTBAT%lineIntersect %ENV_PREF%.rightLine %ENV_PREF%.bottomLine %ENV_PREF%.BR %l2qBAD_THRESH% if ERRORLEVEL 1 exit /B 1 exit /B 0
rem From image %1, writes output %2 rem with circles colour %3 radius %4 strokewidth %5 rem at centres given by env var prefixes %6 onwards. @if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1 @setlocal enabledelayedexpansion @call echoOffSave call %PICTBAT%setInOut %1 nqc if not "%2"=="" if not "%2"=="." set OUTFILE=%2 set COL=%3 if "%COL%"=="." set COL= if "%COL%"=="" set COL=Orange set RAD=%4 if "%RAD%"=="." set RAD= if "%RAD%"=="" set RAD=10 set SW=%5 if "%SW%"=="." set SW= if "%SW%"=="" set SW=1 set sCIRCS= :loop rem echo %6 set CX=!%6.x! set CY=!%6.y! if "%CX%"=="" ( echo %6 has no .x exit /B 1 ) if "%CY%"=="" ( echo %6 has no .y exit /B 1 ) for /F "usebackq" %%L in (`%IM%identify ^ -format "DX=%%[fx:%CX%+%RAD%]" ^ xc:`) do set %%L set sCIRCS=%sCIRCS% circle %CX%,%CY% %DX%,%CY% shift /6 if not "%6"=="" goto loop %IM%convert ^ %INFILE% ^ -fill None ^ -stroke %COL% ^ -strokewidth %SW% ^ -draw "%sCIRCS%" ^ %OUTFILE% call echoRestore @endlocal & set dcOUTFILE=%OUTFILE%
All images on this page were created by the commands shown, using:
%IM%identify -version
Version: ImageMagick 6.9.2-5 Q16 x64 2015-10-31 http://www.imagemagick.org Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC License: http://www.imagemagick.org/script/license.php Visual C++: 180031101 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 to JPG.
Source file for this web page is linreg.h1. To re-create this web page, run "procH1 linreg".
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 5-July-2016.
Page created 21-Jul-2016 18:54:43.
Copyright © 2016 Alan Gibson.