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


9. Программная реализация базового алгоритма Despeckle

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

Для удаления этих крапинок существуют различные алгоритмы Despeckle ("de-speckle" от англ. "speckle" - крапинка, пятнышко).

Есть несколько принципиальных подходов к реализации алгоритма Despeckle, отличающиеся по скорости и точности работы, а также по типам основного применяемого алгоритма и очищаемого шума. Все эти алгоритмы относятся к целому большому направлению, известному как подавление шума.

Я взял уже готовую реализацию алгоритма Despeckle из проекта CxImage и просто "перебил" её под библиотеку FreeImage. В этой реализации используется т.н. медианный фильтр. Принцип его работы достаточно понятно описан здесь.

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

Данная реализация алгоритма Despeckle работает очень медленно - потому что она "каноническая", т.е. наиболее простая. Это очень простой и понятный алгоритм - разобраться в сути его работы сможет любой начинающий. Есть множество вариантов, как сделать данный алгоритм быстрее - рассмотрим их позже.

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

Скачать пакет despec1 (64 КБ)

(Для работы программы требуется FreeImage dll-библиотека из пакета FreeImage DLL v3.9.2 - см. статью 1. Знакомство с FreeImage).

ВАЖНО: Данная реализация алгоритма Deskew работает пока только с чёрно-белыми и серыми изображениями текста. Не пытайтесь подать на вход алгоритма цветные картинки текста. Над устранением этого ограничения подумаем позже.

Рассмотрим исходные коды этой программы:


//	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
// ==========================================================
// Simple despeckle median filter based on the FreeImage library
//
// This code is taken from the CxImage Library at http://www.xdp.it/cximage.htm
// and adapted for the FreeImage Library
//
#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 CxImage_CompareColors(const void *elem1, const void *elem2)
{
	// Comparison function for sorting pixels - needed for the standart qsort function
	
	BYTE* c1 = (BYTE*)elem1;
	BYTE* c2 = (BYTE*)elem2;
	
	int g1 = (int)*c1;
	int g2 = (int)*c2;
	
	return (g2-g1);
}

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

FIBITMAP *CxImage_Median(FIBITMAP *dib, unsigned Ksize)
{	
	// Ksize is a square sliding aperture side size and it must be ONLY an odd value
	// greater than 1, e.g. 3, 5, 7, 9, 11, ...
	
	if (!dib) return false;
	
	// in this implementation let us use only the BW-bitmaps and greyscale for the simplicity
	
	long k2 = Ksize/2;
	long kmax = Ksize-k2;
	long i,j,k;
	
	BYTE* kernel = (BYTE*)calloc(Ksize*Ksize, sizeof(BYTE)); // malloc can not be used here
	//because it does not initialize the allocated buffer with zeroes but calloc does
	
	if (kernel == NULL) return false;	
	
	FIBITMAP *tmp = FreeImage_Clone(dib);
	
	// FreeImage_Clone - makes an exact reproduction of an existing bitmap, including
	// metadata and attached profile if any
	
	if (!tmp) return false;
	
	unsigned xmin,xmax,ymin,ymax;
	
	unsigned margin = k2; // width of the dib border which (border) will not be processed.
	// This is made for the simplicity - the border may be processed separately with a
	// smaller aperture size
	
	xmin = ymin = margin;
	
	xmax = FreeImage_GetWidth(dib) - margin; //FreeImage_GetWidth - Returns the width of 
	//the bitmap in the pixel units.
	
	ymax = FreeImage_GetHeight(dib) - margin; //FreeImage_GetHeight - Returns the height of
	//the bitmap in the pixel units.
	
	for(unsigned y = ymin; y < ymax; y++)
		
		for(unsigned x = xmin; x < xmax; x++)
		{						
			for(j = -k2, i=0;j<kmax;j++)
				
				for(k = -k2;k<kmax;k++, i++)						
					
					if (!FreeImage_GetPixelIndex(dib, x+j, y+k, kernel+i)) return false;

	// FreeImage_GetPixelIndex - Get the pixel index of a palettized image
    	// at position (x, y), including range check (slow access).
					
					qsort(kernel, i, sizeof(BYTE), CxImage_CompareColors);
					
					FreeImage_SetPixelIndex(tmp, x, y, kernel+i/2);

	// FreeImage_SetPixelIndex - Set the pixel index of a palettized image
    	// at position (x, y), including range check (slow access).

		}
		
		free(kernel);
		
		return tmp;
}

////////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------

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 : despec1 <input file name> <aperture size>\n");
		return 0;
	}
	
	unsigned Ksize = atoi(argv[2]);
	
	FIBITMAP *dib = GenericLoader(argv[1], 0);
	
	if (dib)
	{		
		// bitmap successfully loaded!
		
		// despeckle the loaded image
		FIBITMAP *despeckled = CxImage_Median(dib, Ksize);
		
		// save the despeckled bitmap
		const char *output_filename = "despeckled.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, despeckled, output_filename, 0);
		}
		
		// free the loaded FIBITMAP
		FreeImage_Unload(dib);
		
		// free the loaded FIBITMAP
		FreeImage_Unload(despeckled);
		
	}
	
	// call this ONLY when linking with FreeImage as a static library
#ifdef FREEIMAGE_LIB
	FreeImage_DeInitialise();
#endif // FREEIMAGE_LIB
	
	return 0;
} 

Полезные ссылки

В этом разделе собраны все исходники алгоритмов Despeckle, которые мне удалось найти в Интернете. Если у Вас есть какие-то исходники алгоритмов Despeckle - пришлите мне, пожалуйста.

1. CxImage - ещё одна бесплатная графическая библиотека (оттуда и были взяты исходники для данной статьи).

2. Реферат "Программа фильтрации шумов" (простые и понятные исходники)

3. unpaper 0.2 (очень упрощённый подход к Despeckle, но для общего понимания полезно)

4. Old Despeckle Plug-In for The GIMP (довольно интересно, есть смысл позаимствовать идею)

5. XSane (полезно)

6. CMT - Constant-Time Median Filtering.

7. GREYCstoration - Open source algorithm for image denoising and interpolation, using state-of-the-art image processing techniques.

Литература:

1. Быстрые алгоритмы в цифровой обработке изображений. Преобразования и медианные фильтры. Хуанг Т.С.(ред.)

Скачать (3,75 МБ)   В этой книге содержится теоретическая часть по медианным фильтрам.

Hosted by uCoz