Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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.
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
You can’t perform that action at this time.