Вернуться к разделу "Реализация проекта BookScanLib ".


6. FreeImage-функции поворота растра

Функция поворота растра - одна из наиболее значимых для проекта 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 МБ).


FreeImage_RotateClassic

Разрядность:

1 8 24 32

DLL_API FIBITMAP *DLL_CALLCONV FreeImage_RotateClassic(FIBITMAP *dib, double angle);

Эта функция поворачивает 1-битное чёрно-белое, 8-битное серое, 24- или 32-битное цветное изображение при помощи 3 сдвигов (shear).

Угол поворота задаётся параметром в градусах. Вращение производится вокруг центра изображения. Повернутое изображение сохраняет размеры и соотношения сторон исходного изображения - поэтому эту функцию следует использовать при повороте изображения на 90°, 180° или 270°.

warning.gif (1787 bytes) Для 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


FreeImage_RotateEx

Разрядность:

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. Для тестирования возьмём пробное изображение страницы текста:

page.jpg (24868 bytes)

Всего есть 3 наиболее интересных варианта применения FreeImage-функций поворота растра.


Вариант 1.

Командная строка: fi_rot page.jpg 15 1

Результат:

rotated1.jpg (24045 bytes)

В этом случае мы применили функцию FreeImage_RotateClassic и повернули изображение на 15 градусов. Как видно, при этом автоматически создалось более крупное прямоугольное изображение, описанное вокруг нашего повёрнутого изображения, а образовавшиеся "пустые места" в виде треугольников оказались лишены какого-либо цвета (т.е. выглядят как чёрные).

Любопытная деталь: "черный прямоугольник" кажется "накренившимся вправо" - это оптическая иллюзия, связанная с особенностями восприятия глазом разных цветов.

Такой способ поворота выглядит как практически приемлемый для нас. Единственное замечание - "чёрные треугольники" нужно будет закрашивать либо белым, либо цветом фона страницы. Над этой задачей подумаем позже.


Вариант 2.

Командная строка: fi_rot page.jpg 15 2 1

Результат:

rotated2.jpg (24850 bytes)

В этом случае мы применили функцию FreeImage_RotateEx и повернули изображение на 15 градусов. Как видно, при этом размеры и положения сторон исходного изображения не изменились - а наше изображение после поворота оказалось обрезанным. Как  в первом случае, образовавшиеся "пустые места" в виде треугольников оказались лишены какого-либо цвета (т.е. выглядят как чёрные).

Такой способ поворота совершенно неприемлем для нас - т.к. исходное изображение обрезается.


Вариант 3.

Командная строка: fi_rot page.jpg 15 2 0

Результат:

rotated3.jpg (24159 bytes)

В этом случае мы также (как и во 2-м случае) применили функцию FreeImage_RotateEx и повернули изображение на 15 градусов. Размеры и положения сторон исходного изображения опять не изменились - а наше изображение после поворота вновь оказалось обрезанным. Разница лишь в том, что образовавшиеся "пустые места" в виде треугольников оказались заполнены "специальным зеркалирующим приёмом".

Такой способ поворота также совершенно неприемлем для нас - т.к. исходное изображение обрезается.


Выводы:

По результатам экспериментов выяснилось, что функция FreeImage_RotateEx совершенно не подходит для наших целей и задач - т.к. в обоих характерных случаях она обрезает исходное изображение при повороте.

Так что нам следует использовать только функцию FreeImage_RotateClassic и научиться при этом заполнять "пустые места" в виде треугольников соответствующим цветом (как правило, белым).

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

Hosted by uCoz