Вернуться к разделу "Реализация проекта BookScanLib".
При сканировании книг изображения страниц текста практически всегда получаются "зашумленными" многочисленными мелкими крапинками - за счёт микронеровностей бумаги, локальных перепадов яркости при преобразовании из серого в чёрно-белое и т.п.
Для удаления этих крапинок существуют различные алгоритмы 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 МБ) В этой книге содержится теоретическая часть по медианным фильтрам.