Вернуться к разделу "Реализация проекта 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 МБ) В этой книге содержится теоретическая часть по медианным фильтрам.