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


15. Простейший фильтр Adaptive Smoothing

Фильтр Adaptive Smoothing (адаптивное сглаживание) предназначен для контурного сглаживания символов.

Данная реализация является простейшей (и, к сожалению, она довольно некачественна).

Я написал простейшую консольную программу для демонстрации работы фильтра Adaptive Smoothing. На входе она принимает следующие параметры:

ad_smooth <input_file> <radius (double)>

По умолчанию рекомендуются следующие значения:

Radius = 3.0 (обратите внимание - это дробное, а не целое число)

Точность дробных чисел - до одной десятой.

На выходе программа выдаёт этот же файл, обработанный выбранным фильтром.

Программа работает только с серыми и цветными изображениями.

Всё необходимое для тестирования этой программы (компиляционный проект, готовый экзешник, файл-пример и bat-файлы для тестирования программы) я оформил в небольшой пакет:

Скачать пакет ad_smooth (54 КБ)

(Для работы программы требуется 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

// AForge Image Processing Library
// AForge.NET framework
//
// Copyright © Andrew Kirillov, 2005-2007
// andrew.kirillov@gmail.com
//
// Found description in
// "An Edge Detection Technique Using the Facet
// Model and Parameterized Relaxation Labeling"
// by Ioannis Matalas, Student Member, IEEE, Ralph Benjamin, and Richard Kitney

// Adaptive Smoothing - noise removal with edges preserving.

// This algorithm was taken from the AForge.NET framework sourcecodes
// and adopted for the FreeImage library
//
// Copyright (C) 2007-2008:
// monday2000	monday2000@yandex.ru

// double radius = 3.0; // default value

#include "FreeImage.h"
#include "Utilities.h"

FIBITMAP* ProcessFilter(FIBITMAP* src_dib, double radius)
{
	unsigned bpp = FreeImage_GetBPP(src_dib);
	
	unsigned btpp = bpp/8;
	
	unsigned width = FreeImage_GetWidth(src_dib);
	
	unsigned height = FreeImage_GetHeight(src_dib);
	
	unsigned pitch = FreeImage_GetPitch(src_dib);
	
	
	FIBITMAP* dst_dib = FreeImage_Allocate(width, height, bpp);	
	
	BYTE* src_bits = (BYTE*)FreeImage_GetBits(src_dib); // The image raster
	
	BYTE* dst_bits = (BYTE*)FreeImage_GetBits(dst_dib); // The image raster
	
	BYTE* lines, *lined, *linek;

	int size_k = 3;

	int i, j, kin, d;

	BYTE* src_end_row = src_bits + (height-1) * pitch;

	int end_col = width - 1;
		
	// precalculated factor value
	double f = -8 * radius * radius;
	
	// gradient and weights
	double* p_gx = new double[btpp];
	double* p_gy = new double[btpp];
	double* p_weight = new double[btpp];
	double* p_weightTotal = new double[btpp];
	double* p_total = new double[btpp];
		
	for ( int y = 0; y < height; y++ )
	{
		lines = src_bits + y * pitch;
		
		lined = dst_bits + y * pitch;

		for ( int x = 0; x < width; x++ )
		{		
			// original formulas for weight calculation:
			// w(x, y) = exp( -1 * (Gx^2 + Gy^2) / (2 * factor^2) )
			// Gx(x, y) = (I(x + 1, y) - I(x - 1, y)) / 2
			// Gy(x, y) = (I(x, y + 1) - I(x, y - 1)) / 2
			//
			// here is a little bit optimized version

			memset(p_weightTotal, 0, sizeof(double)*btpp);
			memset(p_total, 0, sizeof(double)*btpp);

			// kernel processing
			for (i = 0; i < size_k; i++)
			{
				linek = lines + (i-1) * pitch;

				// 1 aperture y-boundary checkup
				if (linek < src_bits) linek = src_bits;
				if (linek > src_end_row) linek = src_end_row;

				for (j = 0; j < size_k; j++)
				{
					// 2 aperture y-boundary checkup
					if ((linek-pitch) < src_bits) linek = src_bits+pitch;
					if ((linek+pitch) > src_end_row) linek = src_end_row-pitch;

					kin = x+j-1;
					
					// 1 aperture x-boundary checkup
					if (kin < 0) kin = 0;
					if (kin > end_col) kin = end_col;

					// 2 aperture x-boundary checkup
					if ((kin-1) < 0) kin = 1;
					if ((kin+1) > end_col) kin = end_col-1;


					for (d=0; d<btpp; d++)
					{

					p_gx[d] = linek[(kin+1) * btpp + d] - linek[(kin-1) * btpp + d];

					p_gy[d] = (linek + pitch)[kin * btpp + d] - (linek - pitch)[kin * btpp + d];

					p_weight[d] = exp( ( p_gx[d] * p_gx[d] + p_gy[d] * p_gy[d] ) / f );

					p_total[d] += p_weight[d] * linek[kin * btpp + d];

					p_weightTotal[d] += p_weight[d];
					}
				}
			}

			for (d=0; d<btpp; d++)
			{
			if (p_weightTotal[d] == 0.0)

				lined[x * btpp + d] = lines[x * btpp + d];

			else

			// clamp and place result in destination pixel
			lined[x * btpp + d] = (BYTE)MIN(MAX((int)0, (int)(p_total[d] / p_weightTotal[d] + 0.5)), (int)255);
			}
		}
	}

	if(bpp == 8)
	{
		// copy the original palette to the destination bitmap
		
		RGBQUAD *src_pal = FreeImage_GetPalette(src_dib);
		RGBQUAD *dst_pal = FreeImage_GetPalette(dst_dib);
		memcpy(&dst_pal[0], &src_pal[0], 256 * sizeof(RGBQUAD));		
	}
	
	// Copying the DPI...
	
	FreeImage_SetDotsPerMeterX(dst_dib, FreeImage_GetDotsPerMeterX(src_dib));
	
	FreeImage_SetDotsPerMeterY(dst_dib, FreeImage_GetDotsPerMeterY(src_dib));

	delete [] p_gx;
	delete [] p_gy;
	delete [] p_weight;
	delete [] p_weightTotal;
	delete [] p_total;

	return dst_dib;
 }

 ////////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------
/**
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);
	
	FIBITMAP* dib;
	
	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
		dib = FreeImage_Load(fif, lpszPathName, flag);
		
		// unless a bad file format, we are done !
		if (!dib)
		{
			printf("%s%s%s\n","File \"", lpszPathName, "\" not found.");
			return NULL;
		}
	}	
	
	return dib;
}

////////////////////////////////////////////////////////////////////////////////

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 != 3) {
		printf("Usage : ad_smooth <input_file> <radius (double)>\n");
		return 0;
	}
	
	
	FIBITMAP *dib = GenericLoader(argv[1], 0);
	
	double radius = atof(argv[2]);
	
	if (dib)
	{		
		// bitmap is successfully loaded!
		
		if (FreeImage_GetImageType(dib) == FIT_BITMAP)
		{
			if (FreeImage_GetBPP(dib) == 8 || FreeImage_GetBPP(dib) == 24)
			{
				FIBITMAP* dst_dib = ProcessFilter(dib, radius);
				
				if (dst_dib)
				{					
					// save the filtered bitmap
					const char *output_filename = "filtered.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, dst_dib, output_filename, 0);
					}
					
					// free the loaded FIBITMAP
					FreeImage_Unload(dst_dib);					
				}
			}
			
			else
				
				printf("%s\n", "Unsupported color mode.");
		}
		
		else // non-FIT_BITMAP images are not supported.
			
			printf("%s\n", "Unsupported color mode.");
		
		FreeImage_Unload(dib);
	}	 
	
	// call this ONLY when linking with FreeImage as a static library
#ifdef FREEIMAGE_LIB
	FreeImage_DeInitialise();
#endif // FREEIMAGE_LIB
	
	return 0;

Реализация данного алгоритма позаимствована из программной графической библиотеки AForge.NET.

Как видно из исходных кодов, параметр Radius влияет на формирование фактора сглаживания.

Сам алгоритм достаточно оригинален - здесь используется приём, который я назвал условно метод двойной апертуры.

Краткое описание алгоритма:

Обычная квадратная апертура 3х3 пробегает в цикле по всем пикселям изображения. На каждом шаге цикла в другом цикле пробегаем по всем 9 пикселям апертуры и рассматриваем каждый такой пиксель как центр крестообразной апертуры 3х3 (я назвал её условно "капертура"). Вычисляем горизонтальную и вертикальную разницу цветов лучей капертуры, возводим сумму квадратов разниц в экспоненту, получая вес каждого пикселя апертуры. Умножая вес на цвет центрального пикселя апертуры, получаем отклонения.

Если суммарный вес равен нулю, то не изменяем исходный пиксель - иначе делаем его равным результату деления суммарного отклонения на суммарный вес.

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

Hosted by uCoz