Last active
October 3, 2019 14:02
-
-
Save pr3sidentspence/449a54fe8240b4484b402ff8a5ea78d9 to your computer and use it in GitHub Desktop.
A batch file using ImageMagick (https://www.imagemagick.org) to straighten, convert, and trim large bitmap images scanned with a specific (but editable) background color while leaving a some of the background remaining as a border. The fuzz percentage and border left can be given as command line parameters when calling the batch file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ECHO off | |
SETLOCAL ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS | |
REM IMprocessBMPscansOnBlackCarstock.bat | |
REM Usage: IMprocessBMPscansOnBlackCarstock.bat optional:(fuzz[integer] padding[integer]) | |
REM Creates straightened and cropped with padding 100% quality jpgs | |
REM of all BMP files in current directory and places them in a subdirectory | |
REM called output | |
REM Begin init ---------------------------------------------- | |
SET function=init | |
SET "fmtString=[4][11][11][-10][1][-16][1][70]" | |
CALL :db "started" | |
REM fuzz is an ImageMagick parameter given as a percentage that relates | |
REM to how far away from the defined background colour and still be | |
REM considered background. When fuzz is too low for a specific scan, | |
REM too much background is left un-trimmed. When fuzz is too high for | |
REM scan, it will -trim off too much. | |
SET fuzzSetpoint=52 | |
REM The main reason this batch exists is to crop image (post card) scans, | |
REM while leaving some of the background showing. Both to ensure nothing is | |
REM lost for archival reasons, but also to be consistent with the style on | |
REM our digital repository (https://pastforward.winnipeg.ca). | |
SET padding=50 | |
REM These are the RGB space components of the colour of the background we're using. | |
REM Change this here once, if you want. | |
SET red=59 | |
SET green=57 | |
SET blue=55 | |
SET /a "value=1000*(%red%+%green%+%blue%)/(3*255)" | |
SET "imRGB=rgb(%red%,%green%,%blue%)" | |
SET count=0 | |
SET param1=%1 | |
SET param2=%2 | |
SET "inFile=none" | |
SET "rotFile=" | |
SET greatSuccess=0 | |
CMD /C EXIT 0 | |
REM Set up temp and output folders | |
IF NOT EXIST temp\ ( | |
REM CALL :db "Creating temp folder." | |
CALL :db "creating Temp folder." | |
MD temp | |
) ELSE ( | |
CALL :db "temp folder already exists." | |
) | |
IF NOT EXIST output\ ( | |
CALL :db "Creating ouput folder." | |
MD output | |
) ELSE ( | |
CALL :db "output folder already exists." | |
) | |
REM Check for a first parameter and if it exists set fuzz equal to it. | |
IF "%~1" == "" ( | |
CALL :db "No parameters detected, going to :main" | |
GOTO :main | |
) ELSE ( | |
SET "fuzzSetpoint=%1" | |
CALL :db "Found parameter 1 = %1. Attempting to set starting trim fuzz to %param1%." | |
) | |
CALL :db "Starting fuzz now %fuzzSetpoint%." | |
REM Check for a second parameter and if it exists set padding equal to it. | |
IF "%~2" == "" ( | |
CALL :db "No second parameter detected, going to :main" | |
GOTO :main | |
) ELSE ( | |
SET "padding=%2" | |
CALL :db "Found parameter 2 = %2. Attempting to set padding to %2." | |
) | |
CALL :db "Starting padding now %padding%." | |
GOTO :main | |
GOTO :eof | |
REM End init ------------------------------------------------ | |
REM Begin :main ------------------------------------------------- | |
:main | |
SETLOCAL | |
SET function=main | |
CALL :db "started. trim fuzz=%fuzzSetpoint%. padding=%padding%" | |
CALL :testFolder | |
FOR %%a IN (*.bmp) DO ( | |
CALL :batchDumb %%a | |
SET fuzz=%fuzzSetpoint% | |
CALL :rotateOriginal !inFile! | |
CALL :db "rotation and conversion finished. Calling :getTrim with fuzz !fuzz!%%" | |
CALL :getTrim !fuzz! | |
IF ERRORLEVEL 1 ( | |
CALL :db "Attempting next scan." | |
MAGICK convert -background "%imRgb%" -fill red -size 165x100 -pointsize 24 label:FAILED output\!rotFile!.jpg | |
GOTO :main | |
) | |
) | |
CALL :db "deleting !rotFile!." | |
del temp\!rotFile! | |
CALL :db "ended successfully" | |
ENDLOCAL | |
EXIT /B 0 | |
REM End :main ------------------------------------------------- | |
REM Begin :getTrim ------------------------------------------------- | |
REM ImageMagick's -trim (autocropping) feature crops right up to the edge of the image. | |
REM If that's what you want, then set padding=0. | |
REM We want some of the background from the scan to show (%padding% pixels on each side). | |
REM So we call MAGICK -trim on our rotated jpg in temp but tell it just to return what | |
REM it would do, not actually do it, using the -verbose option and sending it's output to null. | |
:getTrim | |
SETLOCAL | |
SET function=getTrim | |
SET localFuzz=%1 | |
FOR /F "tokens=* USEBACKQ delims=>=" %%a IN ( | |
`MAGICK temp\!rotFile! -fuzz %1%% -format "%%@" info:` | |
) DO ( | |
FOR /F "tokens=1 delims=x" %%b IN ("%%a") DO ( | |
IF %%b LEQ 0 ( | |
CMD /C EXIT 0 | |
SET lastFuzz=!fuzz! | |
SET /a fuzz-=1 | |
CALL :db "No image found with trim fuzz =!lastFuzz!%%. Attempting to call myself with CALL :getTrim !fuzz!"" | |
CALL :getTrim !fuzz! | |
EXIT /B 0 | |
) | |
) | |
SET verbose="%%a" | |
FOR /F "tokens=1,2,3,4 delims=x+" %%a in ("!verbose!") DO ( | |
SET /a width=%%a+!padding!+!padding! | |
SET /a height=%%b+!padding!+!padding! | |
SET /a offsetX=%%c-!padding! | |
SET /a offsetY=%%d-!padding! | |
IF !offsetX! LSS 0 (set offsetX=0) | |
IF !offsetY! LSS 0 (set offsetY=0) | |
) | |
) | |
CALL :testTrim !width! !height! !offsetX! !offsetY! %inFile% | |
IF ERRORLEVEL 1 ( | |
CALL :db "testTrim failed with fuzz=%fuzz%%%." | |
IF %fuzz% GEQ 37 ( | |
CMD /C EXIT 0 | |
SET /a fuzz-=1 | |
CALL :db "Lowest fuzz not yet tried. Attempting to call myself with CALL :getTrim !fuzz!"" | |
CALL :getTrim !fuzz! | |
) ELSE ( | |
CALL :db "could not produce a good -trim. Lowest fuzz tried %fuzz%%%." | |
CALL :db "exiting." | |
ENDLOCAL | |
EXIT /B 1 | |
) | |
) ELSE ( | |
CALL :db "Found a good -trim fuzz. Calling :saveOutfile !width!x!height!+!offsetX!+!offsetY!" | |
CALL :saveOutfile !width! !height! !offsetX! !offsetY! | |
CALL :db "exiting." | |
ENDLOCAL | |
EXIT /B 0 | |
) | |
EXIT /B %ERRORLEVEL% | |
REM End :getTrim -------------------------------------------- | |
REM Begin :testTrim ------------------------------------------------- | |
REM Sometimes our best fuzz for trimming most images overtrims for some images. | |
REM Here and in :testLinewe take what :getTrim last told us what would be good | |
REM "trim lines" andtest those lines to see if they are consistently close to our | |
REM background colour (%imRgb%). If all four sides are black then | |
REM return for cropping. If a side isn't black then lower %fuzz% and CALL :getTrim | |
REM again. | |
REM | |
:testTrim | |
SETLOCAL | |
SET function=testTrim | |
CALL :db "started with %1 %2 %3 %4 %5" | |
SET /a "rightX=%1+%3" | |
SET /a "bottomY=%2+%4" | |
CALL :testLine %1 1 %3 %4 top | |
IF ERRORLEVEL 1 ( | |
CALL :db "top trimline not %imRGB%" | |
ENDLOCAL | |
EXIT /B 2 | |
) | |
CALL :testLine 1 %2 %3 %4 left | |
IF ERRORLEVEL 1 ( | |
CALL :db "left trimline not %imRGB%" | |
ENDLOCAL | |
EXIT /B 2 | |
) | |
CALL :testLine %1 1 %3 !bottomY! bottom | |
IF ERRORLEVEL 1 ( | |
CALL :db "bottom trimline not %imRGB%" | |
ENDLOCAL | |
EXIT /B 2 | |
) | |
CALL :testLine 1 %2 !rightX! %4 right | |
IF ERRORLEVEL 1 ( | |
CALL :db "right trimline not %imRGB%" | |
ENDLOCAL | |
EXIT /B 2 | |
) | |
CALL :db "ended successfully" | |
ENDLOCAL | |
EXIT /B 0 | |
REM End :testTrim ------------------------------------------------- | |
REM Begin :testLine ------------------------------------------------- | |
REM Test if 1 pixel wide line along padded auto -trim line is our black background color. | |
REM Our black produces mean values of about 0.22. A small standard of deviation is | |
REM also good, meaning it was consistently 0.22 and not - say - alternating between | |
REM 0.11 and 0.33. | |
:testLine | |
SETLOCAL | |
SET function=testLine | |
CALL :db "started %1 %2 %3 %4 %5" | |
FOR /F "tokens=1,2 USEBACKQ delims=," %%a IN (`MAGICK temp\!rotFile! -crop %1x%2+%3+%4 -format "%%[fx:(mean*1000)],%%[fx:standard_deviation]\n" info:`) DO ( | |
SET mean=%%~na | |
SET /a "delta=%value%-!mean!" | |
If !delta! GEQ 0 (set abDelta=!delta!) ELSE SET /a "abDelta=0-!delta!" | |
IF !abDelta! GEQ 10 ( | |
CALL :db "failed. " | |
ENDLOCAL | |
EXIT /B 1 | |
) | |
) | |
CALL :db "ended. " | |
ENDLOCAL | |
EXIT /B 0 | |
REM End :testLine ------------------------------------------------- | |
REM Begin :db ---------------------------------------------- | |
:db | |
CALL :format %fmtString% %DATE% %TIME% %inFile% " " %function%:%ERRORLEVEL% " " %1 | |
EXIT /B %ERRORLEVEL% | |
REM Begin :db ---------------------------------------------- | |
:format fmt str1 str2 ... -- outputs columns of strings right or left aligned | |
:: -- fmt [in] - format string specifying column width and alignment, i.e. "[-10][10][10]" | |
:$created 20060101 :$changed 20091130 :$categories Echo | |
:$source https://www.dostips.com | |
SET dbErrLvl=%ERRORLEVEL% | |
SETLOCAL | |
set "fmt=%~1" | |
set "line=" | |
set "spac= " | |
set "i=1" | |
for /f "tokens=1,2 delims=[" %%a in ('"echo..%fmt:]=&echo..%"') do ( | |
set /a i+=1 | |
call call set "subst=%%%%~%%i%%%spac%%%%%~%%i%%" | |
if %%b0 GEQ 0 ( | |
call set "subst=%%subst:~0,%%b%%" | |
) ELSE ( | |
call set "subst=%%subst:~%%b%%" | |
) | |
call set "const=%%a" | |
call set "line=%%line%%%%const:~1%%%%subst%%" | |
) | |
echo.%line% | |
EXIT /B %ERRORLEVEL% | |
REM Begin :batchDumb ---------------------------------------------- | |
:batchDumb | |
SET inFile=%1 | |
CALL :db "started. " | |
SET "rotFile=%inFile:~0,-4%-rotated.jpg" | |
SET "outFile=%inFile:~0,-4%.jpg" | |
SETLOCAL | |
SET function=batchDumb | |
CALL :db "ended. Set inFile to %inFile%, rotFile to %rotFile%, and outfile to %outFile%." | |
ENDLOCAL | |
EXIT /B 0 | |
REM Begin :batchDumb ---------------------------------------------- | |
REM Begin :testFolder ---------------------------------------------- | |
:testFolder | |
SETLOCAL | |
SET function=testFolder | |
CALL :db "started. Testing for .bmps." | |
IF NOT EXIST *.bmp ( | |
CALL :db "Folder %CD% contains no bitmap (.bmp) files. Exiting batch." | |
EXIT 1 | |
) | |
ENDLOCAL | |
EXIT /B 0 | |
REM End :testFolder ---------------------------------------------- | |
REM Begin :saveOutfile ---------------------------------------------- | |
:saveOutfile | |
SETLOCAL | |
SET function=saveOutfile | |
MAGICK temp\!rotFile! -crop %1x%2+%3+%4 output\!outFile! | |
CALL :db "saving to temp\!outfile!." | |
ENDLOCAL | |
EXIT /B 0 | |
REM Begin :saveOutfile ---------------------------------------------- | |
REM Begin :rotateOriginal -------------------------------------------- | |
REM ImageMagick didn't like to straighten (-deskew) the large images we have | |
REM so we make a smaller copy, straighten it, and use the rotation value to | |
REM rotate the original. | |
:rotateOriginal | |
SET function=rotateOriginal | |
SETLOCAL | |
SET function=rotateOriginal | |
CALL :db "started. Working on !inFile!." | |
MAGICK !inFile! -resize 2000 temp\smaller.jpg | |
FOR /f "tokens=* USEBACKQ" %%a IN (`MAGICK temp\smaller.jpg -background "%imRGB%" -deskew 20%% -print %%[deskew:angle]\n null:`) DO ( | |
MAGICK !inFile! -background "%imRGB%" -rotate "%%a" -quality "100" temp\!rotFile! | |
CALL :db "ended successfully. ImageMagick rotated %%a degrees." | |
) | |
del temp\smaller.jpg | |
ENDLOCAL | |
EXIT /B 0 | |
REM End :rotateOriginal -------------------------------------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment