Потоковая обрезка изображений

Некоторое время назад, была поставлена задача подготовить процедуру потоковой обработки файлов изображений с отсканированным текстом. Разумеется, на поверхности проблем никаких не обнаружилось: приедут несколько книг в виде картинок, их надо пожать и обрезать, делов-то?

Для этого есть достойное спец. ПО, которое в начилии и функционирует, сотрудники, которые умеют с этим спец. ПО работать и некоторое подобие производственных мощностей, которые легко бы могли потянуть этот проект. Однако же, в реальности, как обычно, проблемы начали приростать к проекту постепенно. Началось всё с того, что этих книг оказалось много, гораздо больше сотни. Позднее выяснилось, что будут предоставлены не исходные изображения со сканера, а уже готовые PDF, с которыми предстоит работать, а количество их приближается к пяти тысячам, с соответствующими объёмами информации (первая партия была в 2.5Тб).

Данный текст описывает процесс решение этой задачи. В общем-то тут ничего особенного с технической точки зрения нет, но здесь будет показано, как изначально не совсем красивое (кривое, tbh) решение в конце-концов превращается в инфернального франкенштейна, которое впоследствии будет угнетать чувство прекрасного у автора.


Содержание:

  1. Знакомство с ImageMagick
  2. Методика обработки изображений
  3. Опыт работы с batch
  4. Скрипт
  5. Заключение
  6. Полезные ресурсы

Знакомство с ImageMagick

image-cropping

Перед началом решения проблемы потоковой обработки изображений я определился с инструментами и общей методикой работы. Мною планировалось применить инструмент ImageMagick, знаменитый и заслуженно популярный инструмент для обработки, создания и редактирования изображений в потоковом режиме, который я уже не раз применял в других проектах. ImageMagick обладает большим числом интерфейсов, для интеграции функционала в свои разработки на различных языках программирования. В том числе, есть такой интерфейс и для python, на котором я изначально и планировал всё это дело реализовать. И уже полученные файлы распознать и поместить в один PDF, который впоследствие можно было бы загружать в web.

Ещё до того, как пришли исходные PDF, из которых нужно было извлекать изображения для их последующей обработки, просто для того, чтобы проверить, как оно будет работать я написал какой-то дурацкий однострочник на batch, копирующий файлы для обработки в рабочую директорию и вызывающий ImageMagick, который по маске *.* обрезал определённое число пикселей от краёв файла (опция -shave). Скрипт показал себя вполне рабочим: всё обрезалось просто моментально, без каких-либо артефактов и пустых файлов на выходе. Как стало понятно позднее, решение написать однострочник на batch, в дальнейшем, сыграло ключевую роль.


Методика обработки изображений

image-cropping

Почему Windows? Как уже было отмечено выше, изначальный план по обработке картинок пришлось несколько изменить из-за того, что для работы были предоставлены распознанные PDF с исходными сканами книг прямо сканера. Эти PDF были созданы, очевидно, из изображений, полученых прямо со сканера, без какой-либо обработки.

Данное предположение основано на том, что всегда, при организации проекта по сканированию большого количества книг, ещё до начала работы, выбирается формат и качество изображений, с которыми в последствии начинается работа: сжатие, распознование, формирование конечного файла-контейрена (pdf, многостраничный tiff, djvu). Обычно, все сканируют сразу в монохромный TIFF, который не сжимают, дабы качество распознавания не ухудшалось и обрезают уже на этапе сканирования (минимум – убирают чёрные поля, остающиеся от стола для сканирования и по несколько миллиметров по краям книги, идеально – центрировать текст на всех изображениях), с помощью функции обрезки, встроенной в ПО. Во всяком случае у нас это именно так и происходит.

Здесь же файлы имели другой формат. Сложно сказать, какой именно, но там однозначно были Grayscale для текста, цветные для изображений в книгах и всегда первые две и последние две страницы книги были цветные. После извлечения изображений из PDF, файлы имели такие параметры:  

Описание Цветные Градации серого
Формат TIFF TIFF
Разрешение 1744x2679 1744x2679
Глубина 8-bit 8-bit
Пространство sRGB Grayscale Gray
Размер 10.6 MB 3,9 MB

 
Соответственно их надо было обработать: из grayscale изображений для текста, надо было сделать качественный monochrome, все цветные изображения пожать в jpeg и обрезать изображения, минимум – обрезать стол сканирования. Центрировать текст на всех изображениях, конечно, вряд ли получилось бы.

Казалось бы, все эти операции можно выполнить на любой ОС, без участия пользователя, однако, после осмотра исходного материала – полученных PDF, пришло понимание, что без участия пользователя эту задачу не выполнить. Просмотрев порядка 20 PDFок, оказалось, что все отсканированные книги имеют различный формат (ин фолио, ин кватро) по высоте и ширине, соотвественно и стол для сканирования занимал то большую, то меньшую часть изображения, которую необходимо было обрезать.

Не все PDF были стандартизированы: встречались и PDF сформированные полностью из цветных изображений, встречались и PDF с цветными изображениями целых разворотов книг. Кроме того, судя по всему, сканирование проводилось без прижимного стекла, вследствие чего некоторые странцы загибались или просто сильно смещались относительноно предыдущих/последующих. Без участия пользователя такие недостатки победить было бы сложно.

Поставляемые для обработки PDF, качественно и сравнительно быстро вполне себе удобно было извлекать с помощью имеющегося у нас лицензионного ПО от Adobe: Adobe Acrobat Pro. А распознавание обработанных изображений планировалось проводить с помощью ABBY FineReader 9 (Tesseract не подходил, потому что исходные книги написаны в дореволюционной орфографии, по той же причине я отказался и от чего-то самописного на python со skimage или тем же pytesseract). Собственно потому и Windows.


Опыт работы с batch

image-cropping

Думаю, что каждый админ хоть как-то соприкасающийся с MS Windows когда-то что-то писал на batch. Я не исключение. На самом деле, в некоторых моментах batch мне нравится даже больше, чем PowerShell. Не могу себя назвать ни гуру PowerShell, ни гуру batch, но тем не менее мнение имею :-) В PowerShell мне не нравится синтаксис. Грубо говоря, там нужно много писать, активно работая tab’ом во время отладки и написания адекватно воспринимаемого/читаемого кода (во всяком случае именно так я делал, когда кропал скрипт для переименовывания и копирования pdf файлов в разных директориях на разной глубине и с разными названиями). batch же в этом плане чуток попроще, чуть более похож на sh, хотя сравнение, конечно же, совсем не корректное. Виндовская консоль вообще про другое. Тем не менее, опыт написания скриптов на batch у меня имелся и я решил продолжить решение поставленной задачи на batch, а не на python, как изначально предполагал.

Причин для этого было несколько:

  • Во-первых, это – время. Как всегда, после постановки задачи, на вопрос: “А когда?”, ответ был: “Вчера. Нужно быстрее.” Так отпал python, потому как весь спектр задач, который предстояло решить я бы не смог сделать на чистом python за несколько дней.
  • Во-вторых, это – общий смысл. В любом случае, основное время работы скрипта занял бы сам ImageMagick, так что оверхед по производительности с использованием batch был бы не такой уж большой.

Скрипт

Изначально, требования ко скрипту были следующими:

  • Скрипт должен был без ошибок обрабатывать поступаемые изображения.
  • Постольку поскольку все изображения разные, обрезать нужно с разных сторон разное число пикселей.
  • Так как обрезание разного числа пикселей с разных сторон без участия оператора невозможно, скрипт должен получать данные о том сколько и откуда обрезать от оператора.
  • На выходе должны оставаться извлечённые из исходных PDF TIFFы.

Однако во время тестовых прогонов скрипта по извлечённым TIFFам к ним добавилось несколько пунктов:

  • Оказалось, что в книгах встречаются цветные изображения (не только 1, 2 и предпоследняя с последней стрраницы), соответственно их надо обнаруживать и пережимать в JPG.
  • Также в извлечённых страницах книг были обнаружены огромные цветные развороты и даже страницы-раскладушки. Их также надо обрабатывать.

Архитектура скрипта

Была придумана условная архитектура скрипта, которую можно изобразить в виде схемы с блоками и зависимостями.

image-cropping

Сам скрипт

Тут хотелось бы для себя оставить небольную заметку о реализации различных алгоритмов на batch. Рабочий скрипт надо обкатать, а потом залить на какой-нибудь github. Займусь этим чуть позже.

  • Создание файлов журнала. Таким вот “элегантным” образом я решил проблему создания файлов логов :-) Что самое интересное, скрипт всего лишь один раз не в нужный файл писал. В остальном отрабатывал на ура. Что довольно странно, учитывая как это дело организовано. LOGNAME - общий лог файл, туда записываются ошибки и этапы работы; FILELOGNAME - лог для наименования отработанных файлов. Мало ли. Пусть будет.
echo %time% Log file was created in %time% at %date% >> "ScriptExecLog_%date%_%time::=-%.txt"
for /f "delims=" %%f in ('dir /b ^| findstr /r /c:"ScriptExecLog"') do set "LOGNAME=%%~dpnxf"
echo %time% Log file was created in %time% at %date% >> "ScriptFileLog_%date%_%time::=-%.txt"
for /f "delims=" %%f in ('dir /b ^| findstr /r /c:"ScriptFileLog"') do set "FILELOGNAME=%%~dpnxf"
  • Вот таким вот образом, осуществлялось перенаправление из stdout в файл, с сохранением вывода в консоль (было важно для интерактива).
 >_ && type _ && type _ >> !LOGNAME! && del _
  • Простейший, на мой взгляд, способ организации внутренней структуры скрипта: на goto. В batch есть возможность это же реализовать и на for, однако, я for применял именно как foreach. Ну и тут же проверка на предмет наличия файлов для работы скрипта. Возвращал ошибку, если не находил файлы и делал ~exit 1 =)
if exist "working\tif" (goto WARNT) else (
echo %time% :: Creating directory 'working\tif'
mkdir working\tif
  • Что самое интересное, почти получилось обойти даже без глобальных переменных. В итоге их было две: 1) для упрощения и большей гибкости в настройки пути нахождения бинарников imagemagick‘а; 2) вот эта – нужна для корректного наименования файлов проекта и последующей обработки этих файлов с отбором их по конкретному имени файла.
for /f "delims=" %%f in ('dir /b *.pdf') do set "original_pdf_name=%%~nf"
  • Тот самый интерактив, где юзер вводит число пикселей, которые нужно обрезать с краёв изображений.
:SET_CROP_SETTINGS
echo %time% :: Collecting settings for crop procedure from user. >> !LOGNAME!
echo:
echo ============================== LEFT page settings ==============================
set /p LEFT_CROP_TOP="Enter number of pixels to cut off from TOP of LEFT page: "
  • После сбора информации, скрипт выводит её пользователю, который либо подтверждает корректность ввода, либо вводит заново. Это было сделано из-за того, что была необходимость минимизировать вероятность холостых запусков скрипта, потому как на больших объёмах время играет ключевую роль, а на переделку проектов времени уходило немало. Тут же, кстати, столкнулся с тем, что batch несмотря на простейшую конструкцию из == yes\no, всё равно принимает любые символы и продолжает выполнение скрипта по первому goto из списка. Т.е. если ввести не yes, а asd он продолжит с goto TIF_FILES_RENAMER.
set /p confirm_crop_settings="Is this settings are correct? (yes/no): "
if "%confirm_crop_settings%"=="yes" (
	goto TIF_FILES_RENAMER
)
if "%confirm_crop_settings%"=="no" (
	goto SET_CROP_SETTINGS
)
  • А вот уже и начало самом скрипта. Довольно оригинальный способ переименовывания файлов, который приводит все файлы к общему стандарту наименования. Как будет показано ниже, местами, наличие стандартизированных имён файлов проекта играет ключевую роль. Тут довольно простая функция: для каждого файла в списке, по очереди высчитывается номер (переменная i), после чего, последовательно добавляется к имени каждого файла. Таким образом, получается структура: название_проекта+порядковый_номер_файла. Вторая функция нужна для избавления от лишних нулей в имени файла.
:TIF_FILES_RENAMER
if exist "*.tif*" (
    set i=0

    for %%f in (*.tif*) do (
        set /a i+=1
        ren "%%f" "!original_pdf_name!_00000!i!.new"
        )

    ren *.new *.tif

    for %%f in (*.tif) do (
        set temp_file_name=%%~nf
        set temp_file_name=!temp_file_name:tmp=!
        set temp_file_name=00000!i!!temp_file_name!
        set temp_file_name=!temp_file_name:~-5!
        set temp_file_name=!original_pdf_name!_!temp_file_name!%%~xf
        ren "%%f" "!temp_file_name!"
        )
) else (
    goto WARNT
)
  • После переименования следует запуск функции сжатия и обрезки файлов (операции с изображениями проще и быстрее проводить с файлам небольшого размера). Тут же проверка на наличие файлов для работы.
:CROP
if exist "*.tif" (call :PAGER) else (
    goto END
)
goto DECISION_MAKING_PROC
  • Собственно функция, вызываемая на предыдущем шагу. Здесь применён нехитрый метод определения разворота обрабатываемой страницы (левый или правый) и в зависимости от результата, вызывалась определённая функция. В функции. Да, batch умеет в nesting, чему я был несказанно рад. Итак, для каждого файла, как и с переименовыванием, брался определённый номер, начиная с нуля, после чего проводилась стандартная процедура проверка чётный-нечётный и основываясь на полученных результатах, принималось решение о принадлежности страницы к левому или правому развороту.
:PAGER
set number_to_decide=0

for %%f in (RuPRLIB*.tif) do (
    set image_name_crop=%%~dpnxf
    set image_wo_ext=%%~nf
    set image_to_work_on=!image_wo_ext!
    set /a number_to_decide+=1
    set /a div_image_number=!number_to_decide!/2
    set /a sum_image_number=!div_image_number!*2
    if !number_to_decide! equ !sum_image_number! (
        call :CROPPER_LEFT
        ) else (
        call :CROPPER_RIGHT
        )
)
goto :eof
  • Вот, две последовательно вызываемые функции для обработки страницы в зависимости от принадлежности к конкретному развороту. Здесь уже вызывается программа convert.exe из пакета ImageMagick. Именно convert.exe стоит применять на больших массивах обрабатываемых данных, потому как она для этого и предназначена. Не стоит заменять её на mogrify.exe.
:CROPPER_LEFT
!IM!\convert.exe !image_name_crop! -depth 1 -monochrome -crop +0+%LEFT_CROP_TOP% +repage !image_to_work_on!_.tif
!IM!\convert.exe !image_to_work_on!_.tif -crop +%LEFT_CROP_LEFT%+0 +repage !image_to_work_on!_.tif
!IM!\convert.exe !image_to_work_on!_.tif -crop -%LEFT_CROP_RIGHT%+0 +repage !image_to_work_on!_.tif
!IM!\convert.exe !image_to_work_on!_.tif -crop +0-%LEFT_CROP_BOTTOM% +repage !image_to_work_on!-cropped.tif
xcopy /y !image_to_work_on!-cropped.tif working\cropped\
del !image_to_work_on!_.tif
del !image_to_work_on!-cropped.tif
goto :eof
:CROPPER_RIGHT
!IM!\convert.exe !image_name_crop! -depth 1 -monochrome -crop +0+%LEFT_CROP_TOP% +repage !image_to_work_on!_.tif
!IM!\convert.exe !image_to_work_on!_.tif -crop +%RIGHT_CROP_LEFT%+0 +repage !image_to_work_on!_.tif
!IM!\convert.exe !image_to_work_on!_.tif -crop -%RIGHT_CROP_RIGHT%+0 +repage !image_to_work_on!_.tif
!IM!\convert.exe !image_to_work_on!_.tif -crop +0-%RIGHT_CROP_BOTTOM% +repage !image_to_work_on!-cropped.tif
xcopy /y !image_to_work_on!-cropped.tif working\cropped\
del !image_to_work_on!_.tif
del !image_to_work_on!-cropped.tif
goto :eof
  • После успешной обработки изображений: перевода всех tif файлов в монохром, включая цветные страницы, и первую с последнией, запускается следующая процедура, которая предполагает уточнение у пользователя: стоит ли проводить автоматический поиск цветных файлов или нет. Обычно, автоматический поиск упрощает обработку файлов проекта, однако, если проект состоит весь из цветных страниц (что встречалось), то это лишь усложнит обработку. Ведь, если обнаружить цветные изображения в автоматическом режиме можно, то для обнаружения типа его разворота, пришлось бы мудрить с очерендной функцией определения разворота, чего не хотелось делать, так как, в среднем, в проекте было всего четыре цветных страницы.
:DECISION_MAKING_PROC
echo %time% :: Getting information if script should search for color images in automatic mode.  
set /p search_for_color_or_not="Should script search for color images in automatic mode? (yes/no): "
if "!search_for_color_or_not!"=="yes" (
    goto COLOR_IMAGE_FINDER
)
if "!search_for_color_or_not!"=="no" (
    goto FIRST_AND_LAST_PAGES
)
  • Далее, в зависимости от ответа пользователя в предыдущей процедуре, вызывается процедура, которая в свою очередь, опять же, проверяет наличие файлов для работы и вызывает функцию обнаружения цветных файлов в автоматическом режиме.
:COLOR_IMAGE_FINDER
if exist "*.tif" (call :PRE_COLOR_IMAGE_WORKER) else (
    goto END
)
goto :eof
  • Однако, до запуска этой функции, пользователю предлагается заполнить ещё одну форму. Здесь от пользователя требуется указать, есть ли в проекте большие цветные развороты на 2-4 страницы (страницы-раскладушки). Если таковые развороты встречались, то они сбивали очерёдность появления левого-правого разворота, поэтому данная опция более актуальна уже на этапе, когда проект разделён на части и большие развороты вынесены в отдельные субпроекты. Их отдельная обработка связана с тем, что скорее всего, указания для обрезания центрированных файлов (первая и последняя страница), не будут иметь актуальность применительно к обработке данных больших разворотов, а посему, пользователю предлагается ввести дополнительные параметры по обрезке разворотов. Если же таковых разворотов в проекте не наблюдается (наличие таких разворотов довольно редкое явление), то пользователь прожатием no просто вызывал искомую функцию автопоиска цветных изображений.
:PRE_COLOR_IMAGE_WORKER
echo %time% :: Are there any huge centered color images in project?
set /p decision_on_center_imgs="Are there any huge centered color images in project? (yes/no): "
if "%decision_on_center_imgs%"=="yes" (
    set /p CENTER_SHAVE_HIGH="Enter number of pixels  to cut off from HIGH of CENTERED image: "
    set /p CENTER_SHAVE_WIDTH="Enter number of pixels to cut off from WIDTH of CENTERED image: "
	call :COLOR_IMAGE_WORKER
)
if "%decision_on_center_imgs%"=="no" (
	call :COLOR_IMAGE_WORKER
)
goto FIRST_AND_LAST_PAGES
  • Процедура поиска и определения цветных файлов проекта. Процедура проходит автоматически, здесь есть небольшая недоработка: в автоматическом режиме не происходит определение разворота страницы. Для того, чтобы её сюда внедрить, пришлось бы опять шерстить весь список файлов, тестить, придумывать что-то… в общем лениво =) К тому же, цветных страниц в проекте, кроме второй и предпоследней, было немного. Часто – не было вовсе. Поиск цветных файлов проводился основываясь на выводе команды !IM!\identify.exe *.tif и “грепанием” её с помощью findstr /r /c:"RGB", далее , её вывод обрабатывался. Берутся первые 21 или 25 символов вывода, которые благодаря стандартизации названий файла всего проекта имею идинаковую длину, после чего всё шло по накатанной. Они определяются в переменную и к каждому из них, в зависимости от разворота (интерактив с оператором), вызывается собственная функция. Если это страницы левого и правого разворотов, то данные по обрезанию беруться из глобальных переменных обрезания разворотов, однако иногда в проекте встречались особые развороты: отцентрированные двойные развороты страниц или даже страницы-гармошки с удлинением вправо-влево или вниз. Для таких особых случаев предусмотрен ещё один интерактив с оператором, где у него уточняются параметры обрезки этих отцентрирвоанных изображений.
:COLOR_IMAGE_WORKER

for /f "delims=" %%f in ('!IM!\identify.exe *.tif ^| findstr /r /c:"RGB"') do (
    set ident_output=%%f
    set color_image=!ident_output:~0,25!
    set color_image_wo_ext=!ident_output:~0,21!
    set working_dir=%%~dpf
    set full_color_image_name=!working_dir!!color_image!
    !IM!\convert.exe !full_color_image_name! -quality 75 !color_image_wo_ext!.jpg
    echo Look over !color_image!.jpg pls.
    set /p manual_r_or_l="Is this image (R)IGHT, (L)EFT or (C)ENTER? (R/L/C): "
    if "!manual_r_or_l!"=="R" (
        echo Page marked as RIGHT, using RIGHT crop method
        call :CROPPER_COLOR_RIGHT
    )
    if "!manual_r_or_l!"=="L" (
        echo Page marked as LEFT, using LEFT crop method
        call :CROPPER_COLOR_LEFT
    )
    if "!manual_r_or_l!"=="C" (
        echo Page marked as CENTER, using CENTER shave method
		call :CROPPER_COLOR_CENTER
    )
)
  • Функции для обработки цветных файлов всех разворотов в ручном режиме.

:CROPPER_COLOR_LEFT
!IM!\convert.exe !color_image_wo_ext!.jpg -crop +0+%LEFT_CROP_TOP% +repage !color_image_wo_ext!_.jpg
!IM!\convert.exe !color_image_wo_ext!_.jpg -crop +%LEFT_CROP_LEFT%+0 +repage !color_image_wo_ext!_.jpg
!IM!\convert.exe !color_image_wo_ext!_.jpg -crop -%LEFT_CROP_RIGHT%+0 +repage !color_image_wo_ext!_.jpg
!IM!\convert.exe !color_image_wo_ext!_.jpg -crop +0-%LEFT_CROP_BOTTOM% +repage !color_image_wo_ext!-cropped.jpg
xcopy /y !color_image_wo_ext!-cropped.jpg working\cropped\
del !color_image_wo_ext!.jpg
del !color_image_wo_ext!_.jpg
del !color_image_wo_ext!-cropped.jpg
goto :eof

:CROPPER_COLOR_RIGHT
!IM!\convert.exe !color_image_wo_ext!.jpg -crop +0+%RIGHT_CROP_TOP% +repage !color_image_wo_ext!_.jpg
!IM!\convert.exe !color_image_wo_ext!_.jpg -crop +%RIGHT_CROP_LEFT%+0 +repage !color_image_wo_ext!_.jpg
!IM!\convert.exe !color_image_wo_ext!_.jpg -crop -%RIGHT_CROP_RIGHT%+0 +repage !color_image_wo_ext!_.jpg
!IM!\convert.exe !color_image_wo_ext!_.jpg -crop +0-%RIGHT_CROP_BOTTOM% +repage !color_image_wo_ext!-cropped.jpg
xcopy /y !color_image_wo_ext!-cropped.jpg working\cropped\
del !color_image_wo_ext!.jpg
del !color_image_wo_ext!_.jpg
del !color_image_wo_ext!-cropped.jpg
goto :eof

:CROPPER_COLOR_CENTER
!IM!\convert.exe !color_image_wo_ext!.jpg -shave !CENTER_SHAVE_WIDTH!x!CENTER_SHAVE_HIGH! +repage !color_image_wo_ext!-cropped.jpg
xcopy /y !color_image_wo_ext!-cropped.jpg working\cropped\
del !color_image_wo_ext!.jpg
del !color_image_wo_ext!_.jpg
del !color_image_wo_ext!-cropped.jpg
goto :eof
  • Процедура поиска и определения первой и последней страницы в проекте, которая в зависимости от наличия исходных файлов TIF, либо возвращает ошибку и завершает выполнения скрипта, либо вызывает функцию FIRST_AND_LAST_PAGE_WORKER.
:FIRST_AND_LAST_PAGES
echo %time% :: Preparing to shave first and last pages of the project.  
if exist "*.tif" (echo %time% :: FIRST_AND_LAST_PAGE_WORKER Function was called && call :FIRST_AND_LAST_PAGE_WORKER) else (
    echo %time% :: FIRST_AND_LAST_PAGES FUNCTON : Something went wrong, files in root directory are not found.  
    goto END
)
goto DIRS
  • Функция по определению первой и последней страницы. Здесь всё также несложно. Берётся список файлов из вывода команды dir /b "RuPRLIB*.tif", после чего этот вывод обрабатывается: берётся первый файл из списка, имя файла записывается в переменную и далее вызвается функция обработчика этого файла. С последним тоже самое, только используется реверсивный вывод dir /b /o-n "RuPRLIB*.tif".
:FIRST_AND_LAST_PAGE_WORKER
set first_file_look_up=0

for /f "delims=" %%f in ('dir /b "RuPRLIB*.tif"') do (
    set /a first_file_look_up+=1
    set first_page_work_dir_tiff=%%~dpf
    set first_page_file_name=%%~nxf
    set first_page_file_name_wo_ext=%%~nf
    if !first_file_look_up! equ 1 (set "first_page=!first_page_work_dir_tiff!!first_page_file_name_wo_ext!") && (call :CROPPER_FIRST_PAGE && :eof)
)

set last_file_look_up=0

for /f "delims=" %%f in ('dir /b /o-n "RuPRLIB*.tif"') do (
    set /a last_file_look_up+=1
    set last_page_work_dir_tiff=%%~dpf
    set last_page_file_name=%%~nxf
	set last_page_file_name_wo_ext=%%~nf
    if !last_file_look_up! equ 1 (set "last_page=!last_page_work_dir_tiff!!last_page_file_name_wo_ext!") && (call :CROPPER_LAST_PAGE && :eof)
)
  • Функции обработки первой и последней страницы.
:CROPPER_FIRST_PAGE
!IM!\convert.exe !first_page!.tif -quality 75 -shave %FIRSTP_CROP_WIDTH%x%FIRSTP_CROP_HIGH% +repage !first_page!-cropped.jpg
xcopy /y !first_page!-cropped.jpg working\cropped\
del !first_page!-cropped.jpg
goto :eof

:CROPPER_LAST_PAGE
!IM!\convert.exe !last_page!.tif -quality 75 -shave %LASTP_CROP_WIDTH%x%LASTP_CROP_HIGH% -blur x0.5 +repage !last_page!-cropped.jpg
xcopy /y !last_page!-cropped.jpg working\cropped\
del !last_page!-cropped.jpg
goto :eof

  • После завершения всех процедур по обработки файлов проекта, вызвается процедура создания конечного древа каталогов.
:DIRS
echo %time% :: Creating final directories for files and copying them into directories.  
if exist "to-pdf" goto WARNS else
echo %time% :: Making 'to-pdf' directory.  
mkdir to-pdf
echo %time% :: Copying files.  
xcopy /y working\cropped\RuPRLIB*.* to-pdf\  >> !FILELOGNAME!
echo %time% :: Files are copied.  
echo %time% :: Making 'uncompressed-source-tiff' directory for source tiff files.  
if exist "uncompressed-source-tiff" goto WARNS else
mkdir uncompressed-source-tiff
echo %time% :: Copying files.  
xcopy /y *.tif uncompressed-source-tiff\  >> !FILELOGNAME!
echo %time% :: Directory was successfully created, all files are copied.  
echo %time% :: Removing uncompressed tiff files from working directory.  
del *.tif
echo %time% :: Removing working directories.  
rmdir /q /s  working
goto FINAL_CLEANER
  • Далее вызвается процедура очистки директорий и удаление цветных файлов-дубликатов, которые имеют расширение jpg и которые будут конфликтовать при распознавании с теми же файлами, только чёрнобелыми tif.
:FINAL_CLEANER
echo %time% :: Final cleaner procedure is about to begin.    
for %%f in (to-pdf\RuPRLIB*.jpg) do (
    set color_file_name_n=%%~nf
    del to-pdf\!color_file_name_n!.tif
    echo %time% :: !color_file_name_n!.tif was removed as a duplicate of a color image.    
)
goto SUCCESS
  • И в завершении скрипта вызвается процедура окончания проекта, которая также высчитывает время выполнения скрипта и записывает в файл журнала. Процедура высчитывания времени выполнения скрипта была нагло стырена отсюда.
:SUCCESS

set end=%time%
set options="tokens=1-4 delims=:.,"

for /f %options% %%a in ("%start%") do (
set start_h=%%a
set /a start_m=100%%b %% 100
set /a start_s=100%%c %% 100
set /a start_ms=100%%d %% 100 )

for /f %options% %%a in ("%end%") do (
set end_h=%%a
set /a end_m=100%%b %% 100
set /a end_s=100%%c %% 100
set /a end_ms=100%%d %% 100 )

set /a hours=%end_h%-%start_h%
set /a mins=%end_m%-%start_m%
set /a secs=%end_s%-%start_s%
set /a ms=%end_ms%-%start_ms%

if %ms% lss 0 set /a secs = %secs% - 1 & set /a ms = 100%ms%
if %secs% lss 0 set /a mins = %mins% - 1 & set /a secs = 60%secs%
if %mins% lss 0 set /a hours = %hours% - 1 & set /a mins = 60%mins%
if %hours% lss 0 set /a hours = 24%hours%
if 1%ms% lss 100 set ms=0%ms%

set /a totalsecs = %hours%*3600 + %mins%*60 + %secs%

echo %time% :: =============== >> !LOGNAME!
echo Elapsed time >> !LOGNAME!
echo Total script execution time: %hours%h:%mins%m:%secs%s.%ms%ms (%totalsecs%.%ms%s total) >> !LOGNAME!
echo %time% :: =============== >> !LOGNAME!
echo %time% :: Job successfully finished.  
endlocal
goto :eof

Заключение

В общем был интересный опыт по обработке изображений, жаль, что всё это не понадобилось. Так бывает.

Полезные ресурсы