Вернуться к разделу "Реализация проекта BookScanLib ".
Функция поворота растра - одна из наиболее значимых для проекта BookScanLib. В библиотеке FreeImage уже реализованы 2 такие функции:
Рассмотрим эти функции более подробно.
Я написал простейшую консольную программу для изучения работы этих 2 функций. На входе она принимает имя графического файла, угол поворота (в градусах), номер функции (1 или 2) и параметр маски (только для 2-ой функции).
Всё необходимое для тестирования этой программы (компиляционный проект, готовый экзешник, файл-пример и bat-файлы для тестирования программы) я оформил в небольшой пакет:
Скачать пакет fi_rot (43 КБ)
(Для работы программы требуется FreeImage dll-библиотека из пакета FreeImage DLL v3.9.2 - см. статью 1. Знакомство с FreeImage).
Рассмотрим исходные коды этой программы:
// This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // http://www.gnu.org/copyleft/gpl.html // Copyright (C) 2007-2008: // monday2000 monday2000@yandex.ru // ========================================================== // This is a FreeImage 3.92 Rotate Example // // Rotate Bitmap Demo // // ========================================================== // // This example shows how to rotate a graphical file from the hard disk using the FreeImage // Library // // ========================================================== #include <assert.h> #include <stdio.h> #include <stdlib.h> #include "FreeImage.h" // ---------------------------------------------------------- /** FreeImage error handler @param fif Format / Plugin responsible for the error @param message Error message */ void FreeImageErrorHandler(FREE_IMAGE_FORMAT fif, const char *message) { printf("\n*** "); printf("%s Format\n", FreeImage_GetFormatFromFIF(fif)); printf(message); printf(" ***\n"); } // ---------------------------------------------------------- /** Generic image loader @param lpszPathName Pointer to the full file name @param flag Optional load flag constant @return Returns the loaded dib if successful, returns NULL otherwise */ FIBITMAP* GenericLoader(const char* lpszPathName, int flag) { FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; // check the file signature and deduce its format // (the second argument is currently not used by FreeImage) fif = FreeImage_GetFileType(lpszPathName, 0); if(fif == FIF_UNKNOWN) { // no signature ? // try to guess the file format from the file extension fif = FreeImage_GetFIFFromFilename(lpszPathName); } // check that the plugin has reading capabilities ... if((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif)) { // ok, let's load the file FIBITMAP *dib = FreeImage_Load(fif, lpszPathName, flag); // unless a bad file format, we are done ! return dib; } return NULL; } // ---------------------------------------------------------- int main(int argc, char *argv[]) { // call this ONLY when linking with FreeImage as a static library #ifdef FREEIMAGE_LIB FreeImage_Initialise(); #endif // FREEIMAGE_LIB // initialize your own FreeImage error handler FreeImage_SetOutputMessage(FreeImageErrorHandler); if(argc < 4) { printf("Usage : fi_rot <input file name> <angle in degrees> \ <rotation function (1 or 2)> <rotation mask (0 or 1)>\n"); return 0; } int angle = atoi(argv[2]); if (angle == 0) {printf("The second parameter must be a value\n"); return -1;} int function = atoi(argv[3]); if (function != 1 && function != 2) {printf("The third parameter must be 1 or 2\n"); return -1;} int mask; if(argc == 5) // "mask" parameter - for the FreeImage_RotateEx function only { mask = atoi(argv[4]); if (mask != 0 && mask != 1) {printf("The fourth parameter must be 0 or 1\n"); return -1;} } FIBITMAP *dib = GenericLoader(argv[1], 0); if (dib) { // bitmap successfully loaded! // ========================================================== // Rotation is here: // Rotation option #1: // This function is usually used to rotate a bitmap bу 90°,180° or 270° only (CCW rotation) FIBITMAP *rotated; if (function == 1) { printf("Using FreeImage_RotateClassic, angle = %d\n", angle); rotated = FreeImage_RotateClassic(dib, angle); } // ---------------------------------------------------------- // Rotation option #2: // rotate the image about the center of the image area if (function == 2) { double x_orig = FreeImage_GetWidth(dib) / (double)2; double y_orig = FreeImage_GetHeight(dib) / (double)2; // perform a CCW rotation printf("Using FreeImage_RotateEx, angle = %d, mask = %s\n", angle,mask?"TRUE":"FALSE"); rotated = FreeImage_RotateEx(dib, angle, 0, 0, x_orig, y_orig, mask); } // ========================================================== // save the rotated bitmap const char *output_filename = "rotated.tif"; // first, check the output format from the file name or file extension FREE_IMAGE_FORMAT out_fif = FreeImage_GetFIFFromFilename(output_filename); if(out_fif != FIF_UNKNOWN) { // then save the file FreeImage_Save(out_fif, rotated, output_filename, 0); } // free the loaded FIBITMAP FreeImage_Unload(dib); // free the rotated FIBITMAP FreeImage_Unload(rotated); } // call this ONLY when linking with FreeImage as a static library #ifdef FREEIMAGE_LIB FreeImage_DeInitialise(); #endif // FREEIMAGE_LIB return 0; } |
Примечание: более подробную информацию о библиотеке FreeImage и об используемых в ней функциях смотрите в её англоязычной документации FreeImage Documentation (1,99 МБ).
Разрядность:
1 | 8 | 24 | 32 |
DLL_API FIBITMAP *DLL_CALLCONV FreeImage_RotateClassic(FIBITMAP *dib, double angle);
Эта функция поворачивает 1-битное чёрно-белое, 8-битное серое, 24- или 32-битное цветное изображение при помощи 3 сдвигов (shear).
Угол поворота задаётся параметром в градусах. Вращение производится вокруг центра изображения. Повернутое изображение сохраняет размеры и соотношения сторон исходного изображения - поэтому эту функцию следует использовать при повороте изображения на 90°, 180° или 270°.
Для 1-битных чёрно-белых изображений угол поворота может быть только кратным 90° (например –90, 90, 180, 270). Для других значений возвращается значение NULL. |
Ссылки
Paeth A., A Fast Algorithm for General Raster Rotation. Graphics Gems, p. 179, Andrew Glassner editor, Academic Press, 1990.
Yariv E., High quality image rotation (rotate by shear). [Online] http://www.codeproject.com/bitmap/rotatebyshear.asp
Разрядность:
8 | 24 | 32 |
DLL_API FIBITMAP *DLL_CALLCONV FreeImage_RotateEx(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask);
Эта функция выполняет поворот и / или преобразование 8-битного серого, 24- или 32-битного изображения, используя кубический B-сплайн 3-порядка. Повёрнутое изображение будет иметь ту же ширину и высоту, что и исходное изображение, так что эта функция больше всего подходит для компьютерного зрения и робототехники.
Угол поворота задаётся параметром в градусах. Горизонтальные и вертикальные преобразования (translations) изображения (в пикселях) задаются параметрами x_shift и y_shift. Вращение производится вокруг центра, задаваемого параметрами x_origin and y_origin (также в пикселях).
Когда параметр use_mask равен TRUE, несущественные части изображения запоняются чёрным, в противном случае для их заполнения применяется специальный зеркалирующий приём.
Ссылки
Philippe Thevenaz, Spline interpolation, a C source code implementation. [Online] http://bigwww.epfl.ch/thevenaz/
Unser M., Splines: A Perfect Fit for Signal and Image Processing. IEEE Signal Processing Magazine, vol. 16, no. 6, pp. 22-38, November 1999.
Unser M., Aldroubi A., Eden M., B-Spline Signal Processing: Part I--Theory. IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 821-832, February 1993.
Unser M., Aldroubi A., Eden M., B-Spline Signal Processing: Part II--Efficient Design and Applications. IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 834-848, February 1993.
Рассмотрим наиболее интересные варианты использования функций FreeImage_RotateClassic и FreeImage_RotateEx. Для тестирования возьмём пробное изображение страницы текста:
Всего есть 3 наиболее интересных варианта применения FreeImage-функций поворота растра.
Командная строка: fi_rot page.jpg 15 1
В этом случае мы применили функцию FreeImage_RotateClassic и повернули изображение на 15 градусов. Как видно, при этом автоматически создалось более крупное прямоугольное изображение, описанное вокруг нашего повёрнутого изображения, а образовавшиеся "пустые места" в виде треугольников оказались лишены какого-либо цвета (т.е. выглядят как чёрные).
Любопытная деталь: "черный прямоугольник" кажется "накренившимся вправо" - это оптическая иллюзия, связанная с особенностями восприятия глазом разных цветов.
Такой способ поворота выглядит как практически приемлемый для нас. Единственное замечание - "чёрные треугольники" нужно будет закрашивать либо белым, либо цветом фона страницы. Над этой задачей подумаем позже.
Командная строка: fi_rot page.jpg 15 2 1
В этом случае мы применили функцию FreeImage_RotateEx и повернули изображение на 15 градусов. Как видно, при этом размеры и положения сторон исходного изображения не изменились - а наше изображение после поворота оказалось обрезанным. Как в первом случае, образовавшиеся "пустые места" в виде треугольников оказались лишены какого-либо цвета (т.е. выглядят как чёрные).
Такой способ поворота совершенно неприемлем для нас - т.к. исходное изображение обрезается.
Командная строка: fi_rot page.jpg 15 2 0
В этом случае мы также (как и во 2-м случае) применили функцию FreeImage_RotateEx и повернули изображение на 15 градусов. Размеры и положения сторон исходного изображения опять не изменились - а наше изображение после поворота вновь оказалось обрезанным. Разница лишь в том, что образовавшиеся "пустые места" в виде треугольников оказались заполнены "специальным зеркалирующим приёмом".
Такой способ поворота также совершенно неприемлем для нас - т.к. исходное изображение обрезается.
По результатам экспериментов выяснилось, что функция FreeImage_RotateEx совершенно не подходит для наших целей и задач - т.к. в обоих характерных случаях она обрезает исходное изображение при повороте.
Так что нам следует использовать только функцию FreeImage_RotateClassic и научиться при этом заполнять "пустые места" в виде треугольников соответствующим цветом (как правило, белым).
Также мы выяснили, что поворачивать на произвольный угол можно только серые и цветные изображения, а чёрно-белые (т.е. 1-битные) - нельзя (их можно поворачивать только на угол, кратный 90 градусам). Так что чёрно-белые изображения прийдётся преобразовывать в серые перед поворотом на произвольный угол, а после поворота - обратно в чёрно-белые.