GDI+有Bitmap类。
應用例子:
有關GDI+對bmp的處理
數字圖像處理算法實現?
[ 作者:admin | 轉貼自:本站原創 | 點擊數:634 | 更新時間:2004-12-31 | 文章錄入:admin ]?
摘要: 關于空間域圖像處理算法框架,直方圖處理,空間域濾波器算法框架的編程心得,使用GDI+(C++)
一,圖像文件的讀取
初學數字圖像處理時,圖像文件的讀取往往是一件麻煩的事情,我們要面對各種各樣的圖像文件格式,如果僅用C++的fstream庫那就必須了解各種圖像編碼格式,這對于初學圖像處理是不太現實的,需要一個能幫助輕松讀取各類圖像文件的庫。在Win32平臺上GDI+(C++)是不錯的選擇,不光使用上相對于Win32 GDI要容易得多,而且也容易移植到.Net平臺上的GDI+。
Gdiplus::Bitmap類為我們提供了讀取各類圖像文件的接口,Bitmap::LockBits方法產生的BitmapData類也為我們提供了高速訪問圖像文件流的途徑。這樣我們就可以將精力集中于圖像處理算法的實現,而不用關心各種圖像編碼。具體使用方式請參考MSDN中GDI+文檔中關于Bitmap類和BitmapData類的說明。另外GDI+僅在Windows XP/2003上獲得直接支持,對于Windows 2000必須安裝相關DLL,或者安裝有Office 2003,Visual Studio 2003 .Net等軟件。
二,空間域圖像處理算法框架
(1) 在空間域圖像處理中,對于一個圖像我們往往需要對其逐個像素的進行處理,對每個像素的處理使用相同的算法(或者是圖像中的某個矩形部分)。即,對于圖像f(x,y),其中0≤x≤M,0≤y≤N,圖像為M*N大小,使用算法algo,則f(x,y) = algo(f(x,y))。事先實現一個算法框架,然后再以函數指針或函數對象(functor,即實現operator()的對象)傳入算法,可以減輕編程的工作量。
如下代碼便是一例:
#ifndef PROCESSALGO_H
#define PROCESSALGO_H
#include <windows.h>
#include <Gdiplus.h>
?
namespace nsimgtk
{
template <typename pixelType, Gdiplus::PixelFormat pixelFormat, class Processor>
bool ProcessPixelsOneByOne(Gdiplus::Bitmap* const p_bitmap, Processor processor, unsigned int x, unsigned int y,
unsigned int width, unsigned int height)
{
if (p_bitmap == NULL)
{
return false;
}
if ((width + x > p_bitmap->GetWidth()) || (height + y >p_bitmap->GetHeight()))
{
return false;
}
Gdiplus::BitmapData bitmapData;
Gdiplus::Rect rect(x, y, width,height);
if (p_bitmap->LockBits(&rect, Gdiplus::ImageLockModeWrite, pixelFormat, &bitmapData) != Gdiplus::Ok)
{
return false;
}
pixelType *pixels = (pixelType*)bitmapData.Scan0;
?
for (unsigned int row=0; row<height; ++row)
{
for (unsigned int col=0; col<width; ++col)
{
processor(&pixels[col+row*bitmapData.Stride/sizeof(pixelType)]);
}
}
if (p_bitmap->UnlockBits(&bitmapData) != Gdiplus::Ok)
{
return false;
}
return true;
}
}
#endif
ProcessPixelsOneByOne函數可以對圖像中從(x,y)位置起始,width*height大小的區域進行處理。模板參數pixelType用于指定像素大小,例如在Win32平臺上傳入unsigned char即為8位,用于8階灰度圖。模板參數Processor為圖像處理算法實現,可以定義類實現void operator(pixelType *)函數,或者傳入同樣接口的函數指針。
如下便是一些算法示例(說明見具體注釋):
#ifndef SPATIALDOMAIN_H
#define SPATIALDOMAIN_H
#include <cmath>
#include <string>
namespace nsimgtk
{
// 8階灰度圖的灰度反轉算法
class NegativeGray8
{
public:
void operator()(unsigned char *const p_value)
{
*p_value ^= 0xff;
}
};
// 8階灰度圖的Gamma校正算法
class GammaCorrectGray8
{
private:
unsigned char d_s[256];
public:
GammaCorrectGray8::GammaCorrectGray8(double c, double gamma);
void operator()(unsigned char*const p_value)
{
*p_value = d_s[*p_value];
}
};
// 8階灰度圖的飽和度拉伸算法
class ContrastStretchingGray8
{
private:
unsigned char d_s[256];
public:
ContrastStretchingGray8::ContrastStretchingGray8(double a1, double b1, unsigned int x1,
double a2, double b2, unsigned int x2, double a3, double b3);
void operator()(unsigned char*const p_value)
{
*p_value = d_s[*p_value];
}
};
// 8階灰度圖的位平面分割,構造函數指定位平面號
class BitPlaneSliceGray8
{
private:
unsigned char d_s[256];
public:
BitPlaneSliceGray8(unsigned char bitPlaneNum);
void operator()(unsigned char* const p_value)
{
*p_value = d_s[*p_value];
}
};
}
#endif
// 上述類中各構造函數的實現代碼,應該分在另一個文件中,此處為說明方便,一并列出
#include "SpatialDomain/spatialDomain.h"
namespace nsimgtk
{
GammaCorrectGray8::GammaCorrectGray8(double c, double gamma)
{
double temp;
for (unsigned int i=0; i<256; ++i)
{
temp = ceil(c * 255.0 * pow(double(i)/255.0, gamma));
d_s[i] = unsigned char(temp);
}
}
ContrastStretchingGray8::ContrastStretchingGray8(double a1, double b1, unsigned int x1,
double a2, double b2, unsigned int x2, double a3, double b3)
{
if (x1 > 255 || x2 > 255 || x1 > x1)
{
for (unsigned int i=0; i<256; ++i)
d_s[i] = i;
}
else
{
double tmp;
for (unsigned int i=0; i<x1; ++i)
{
tmp = ceil(a1*double(i)+b1);
d_s[i] = (unsigned char)tmp;
}
for (unsigned int i=x1; i<x2; ++i)
{
tmp = ceil(a2*double(i)+b2);
d_s[i] = (unsigned char)tmp;
}
for (unsigned int i=x2; i<256; ++i)
{
tmp = ceil(a3*double(i)+b3);
d_s[i] = (unsigned char)tmp;
}
}
}
BitPlaneSliceGray8::BitPlaneSliceGray8(unsigned char bitPlaneNum)
{
unsigned char bitMaskArray[8] =
{
0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80
};
for (unsigned int i=0; i<256; ++i)
{
unsigned char tmp = i;
tmp &= bitMaskArray[bitPlaneNum];
tmp = (tmp >> bitPlaneNum) * 255;
d_s[i] = tmp;
}
}
}
(2) 直方圖在GDI+1.0中沒有獲得支持,我們必須自行實現。直方圖相關的處理在數字圖像處理中占有重要地位,可以通過它獲取圖像灰度級的統計信息,且可以通過直方圖進行一些重要的圖像增強技術,如直方圖均衡化,直方圖規定化,基本全局門限等。
下面是獲取8階圖像直方圖的算法實現:
namespace nsimgtk
{
bool GetHistogramNormalizeGray8(Gdiplus::Bitmap * const p_bitmap, float *histogramArray)
{
if (p_bitmap == NULL || histogramArray == NULL)
{
return false;
}
Gdiplus::BitmapData bitmapData;
Gdiplus::Rect rect(0, 0, p_bitmap->GetWidth(), p_bitmap->GetHeight());
if (p_bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat8bppIndexed, &bitmapData) != Gdiplus::Ok)
{
return false;
}
unsigned char *pixels = (unsigned char*)bitmapData.Scan0;
unsigned int histogram[256];
for (int i=0; i<256; ++i)
{
histogram[i] = 0;
}
for (unsigned int row=0; row<p_bitmap->GetWidth(); ++row)
{
for (unsigned int col=0; col<p_bitmap->GetHeight(); ++col)
{
++histogram[pixels[col+row*bitmapData.Stride]];
}
}
const unsigned int totalPixels = p_bitmap->GetWidth() * p_bitmap->GetHeight();
for (int i=0; i<256; ++i)
{
histogramArray[i] = float(histogram[i]) / float(totalPixels);
}
if (p_bitmap->UnlockBits(&bitmapData) != Gdiplus::Ok)
{
return false;
}
return true;
}
}
在獲取直方圖后(即上面算法的第二個參數),再將其作為參數傳入下面的對象的構造函數,然后以該對象為仿函數傳入ProcessPixelsOneByOne即可實現8階圖像直方圖均衡化:
#ifndef SPATIALDOMAIN_H
#define SPATIALDOMAIN_H
#include <cmath>
#include <string>
namespace nsimgtk
{
// 8階灰度圖的直方圖均衡化
class HistogramEqualizationGray8
{
private:
unsigned char d_s[256];
public:
HistogramEqualizationGray8(const float *const histogramArray);
void operator()(unsigned char *const p_value)
{
*p_value = d_s[*p_value];
}
};
}
#endif
//
#include "SpatialDomain/spatialDomain.h"
namespace nsimgtk
{
HistogramEqualizationGray8::HistogramEqualizationGray8(const float *const histogramArray)
{
if (histogramArray != NULL)
{
float sum = 0.0;
for (int i=0; i<256; ++i)
{
sum += histogramArray[i];
d_s[i] = unsigned char(sum * 255);
}
}
}
}
(3)空間域濾波器,濾波器是一個m*n大小的掩模,其中m,n均為大于1的奇數。濾波器逐像素地通過圖像的全部或部分矩形區域,然后逐像素地對掩模覆蓋下的像素使用濾波器算法獲得響應,將響應賦值于當前像素即掩模中心像素,另外濾波器算法使用中將會涉及到圖像邊緣的問題,這可以對邊緣部分掩模使用補零法補齊掩模下無像素值的區域,或者掩模的移動范圍以不越出圖像邊緣的方式移動,當然這些處理方法都會給圖像邊緣部分帶來不良效果,但是一般情況下,圖像邊緣部分往往不是我們關注的部分或者沒有重要的信息。
下面的濾波器算法框架SpatialFilterAlgo即以補零法(zero-padding)實現:
#ifndef SPATIALFILTER_H
#define SPATIALFILTER_H
#include <vector>
#include <numeric>
#include <algorithm>
#include <gdiplus.h>
#include <fstream>
#include <cmath>
namespace nsimgtk
{
template <typename pixelType, Gdiplus::PixelFormat pixelFormat, class FilterMask>
bool SpatialFilterAlgo(Gdiplus::Bitmap* const p_bitmap, FilterMask filterMask, unsigned int x, unsigned int y,
unsigned int width, unsigned int height)
{
if (p_bitmap == NULL)
{
return false;
}
if ((width + x > p_bitmap->GetWidth()) || (height + y >p_bitmap->GetHeight()))
{
return false;
}
Gdiplus::BitmapData bitmapData;
Gdiplus::Rect rect(x, y, width,height);
if (p_bitmap->LockBits(&rect, Gdiplus::ImageLockModeWrite, pixelFormat, &bitmapData) != Gdiplus::Ok)
{
return false;
}
pixelType *pixels = (pixelType*)bitmapData.Scan0;
const unsigned int m = filterMask.d_m; // mask's width
const unsigned int n = filterMask.d_n; // mask's height
std::vector<pixelType> tmpImage((m-1+width)*(n-1+height)); // extend image to use zero-padding
// copy original bitmap to extended image with zero-padding method
for (unsigned int row=0; row<height; ++row)
{
for (unsigned int col=0; col<width; ++col)
{
tmpImage[(col+m/2)+(row+n/2)*(bitmapData.Stride/sizeof(pixelType)+m-1)] =
pixels[col+row*bitmapData.Stride/sizeof(pixelType)];
}
}
// process every pixel with filterMask
for (unsigned int row=0; row<height; ++row)
{
for (unsigned int col=0; col<width; ++col)
{
// fill the "m*n" mask with the current pixel's neighborhood
for (unsigned int i=0; i<n; ++i)
{
for (unsigned int j=0; j<m; ++j)
{
filterMask.d_mask[i*m+j] = tmpImage[(col+j)+(row+i)*(bitmapData.Stride/sizeof(pixelType)+m-1)];
}
}
// replace the current pixel with filter mask's response
pixels[col+row*bitmapData.Stride/sizeof(pixelType)] = filterMask.response();
}
}
if (p_bitmap->UnlockBits(&bitmapData) != Gdiplus::Ok)
{
return false;
}
return true;
}
}
#endif
其中模板參數FilterMask即為濾波掩模算法。通常的濾波算法有均值濾波器,可以模糊化圖像,去除圖形中的細節部分,使得我們可以關注圖像中較為明顯的部分,均值濾波器用于周期性噪聲。中值濾波器用于圖像中存在椒鹽噪聲也即脈沖噪聲的情況下。另外有基于一階微分的Sobel梯度算子和基于兩階微分的拉普拉斯算子,它們往往被用于邊緣檢測中。
下面是一些濾波器算法的具體實現,所以濾波器算法都應該實現pixelType response()函數以及有d_mask,d_m,d_n成員,這可以通過繼承__filteMask類獲得(不需要付出虛函數代價)。
#ifndef SPATIALFILTER_H
#define SPATIALFILTER_H
#include <vector>
#include <numeric>
#include <algorithm>
#include <gdiplus.h>
#include <fstream>
#include <cmath>
namespace nsimgtk
{
// 濾波器掩模的基類,提供掩模大小d_m,d_n,掩模覆蓋下的m*n個像素值d_mask
// others filterMask should inherit it
template <typename pixelType>
struct __filterMask
{
const unsigned int d_m;
const unsigned int d_n;
// image's pixels under the m*n filter mask
std::vector<pixelType> d_mask;
// filter mask's width and heigh must be a odd, if not, it will plus one for the width or the height
__filterMask(unsigned int m, unsigned int n)
: d_m(m%2 ? m:m+1), d_n(n%2 ? n:n+1), d_mask(d_m*d_n)
{
}
};
// 掩模權值為全1的均值濾波器
template <typename pixelType>
class averagingFilterMaskSp
: public __filterMask<pixelType>
{
public:
averagingFilterMaskSp(unsigned int m, unsigned int n)
: __filterMask<pixelType>(m, n)
{ }
pixelType response()
{
return std::accumulate(d_mask.begin(), d_mask.end(), 0) / (d_m * d_n);
}
};
// 可自定義掩模權值的均值濾波器
template <typename pixelType>
class averagingFilterMask
: public __filterMask<pixelType>
{
private:
std::vector<pixelType> d_weight; // weights' vector(m*n)
int d_weight_sum; // all weights' sum
public:
averagingFilterMask(unsigned int m, unsigned int n, const std::vector<pixelType>& weightVec)
: __filterMask<pixelType>(m, n), d_weight(weightVec)
{
if (weightVec.size() != d_mask.size())
{
// if weight's size isn't equal to mask's size, it will change filter mask as a special filter mask
d_weight.resize(d_mask.size(), 1);
}
d_weight_sum = std::accumulate(d_weight.begin(), d_weight.end(), 0);
}
pixelType response()
{
return std::inner_product(d_mask.begin(), d_mask.end(), d_weight.begin(), 0) / d_weight_sum;
}
};
// 中值濾波器
template <typename pixelType>
class medianFilterMask
: public __filterMask<pixelType>
{
public:
medianFilterMask(unsigned int m, unsigned int n)
: __filterMask<pixelType>(m, n)
{ }
pixelType response()
{
std::sort(d_mask.begin(), d_mask.end());
return d_mask[d_mask.size()/2];
}
};
// 3*3拉普拉斯濾波器
// the mask is: [0 1 0 [0 -1 0
// 1 -5 1 or -1 5 -1
// 0 1 0] 0 -1 0]
// if pixel's brightness is less than min, set it to min
// if pixel's brightness is larger than max, set it to max
template <typename pixelType, pixelType min, pixelType max>
class laplacianFilter
: public __filterMask<pixelType>
{
public:
laplacianFilter()
: __filterMask<pixelType>(3, 3)
{ }
pixelType response()
{
int ret = (int)(5*(int)d_mask[4]) - ((int)d_mask[5]+d_mask[3]+d_mask[1]+d_mask[7]);
if (ret < min)
ret = min;
if (ret > max)
ret = max;
return ret;
}
};
// 3*3Sobel濾波器
// the mask is: [-1 -2 -1 [-1 0 1
// 0 0 0 and -2 0 2
// 1 2 1] -1 0 1]
// if pixel's brightness is larger than max, set it to max
template <typename pixelType, pixelType max>
class sobelFilter
: public __filterMask<pixelType>
{
public:
sobelFilter()
: __filterMask<pixelType>(3, 3)
{ }
pixelType response()
{
int ret = ::abs(d_mask[6]+2*d_mask[7]+d_mask[8]-d_mask[0]-2*d_mask[1]-d_mask[2])
+ ::abs(d_mask[2]+2*d_mask[5]+d_mask[8]-d_mask[0]-2*d_mask[3]-d_mask[6]);
if (ret > max)
ret = max;
return ret;
}
};
}
#endif
?
數字圖像處理算法實現
------------編程心得(1)
2001414班 朱偉 20014123
摘要: 關于空間域圖像處理算法框架,直方圖處理,空間域濾波器算法框架的編程心得,使用GDI+(C++)
一,圖像文件的讀取
初學數字圖像處理時,圖像文件的讀取往往是一件麻煩的事情,我們要面對各種各樣的圖像文件格式,如果僅用C++的fstream庫那就必須了解各種圖像編碼格式,這對于初學圖像處理是不太現實的,需要一個能幫助輕松讀取各類圖像文件的庫。在Win32平臺上GDI+(C++)是不錯的選擇,不光使用上相對于Win32 GDI要容易得多,而且也容易移植到.Net平臺上的GDI+。
Gdiplus::Bitmap類為我們提供了讀取各類圖像文件的接口,Bitmap::LockBits方法產生的BitmapData類也為我們提供了高速訪問圖像文件流的途徑。這樣我們就可以將精力集中于圖像處理算法的實現,而不用關心各種圖像編碼。具體使用方式請參考MSDN中GDI+文檔中關于Bitmap類和BitmapData類的說明。另外GDI+僅在Windows XP/2003上獲得直接支持,對于Windows 2000必須安裝相關DLL,或者安裝有Office 2003,Visual Studio 2003 .Net等軟件。
二,空間域圖像處理算法框架
(1) 在空間域圖像處理中,對于一個圖像我們往往需要對其逐個像素的進行處理,對每個像素的處理使用相同的算法(或者是圖像中的某個矩形部分)。即,對于圖像f(x,y),其中0≤x≤M,0≤y≤N,圖像為M*N大小,使用算法algo,則f(x,y) = algo(f(x,y))。事先實現一個算法框架,然后再以函數指針或函數對象(functor,即實現operator()的對象)傳入算法,可以減輕編程的工作量。
如下代碼便是一例:
#ifndef PROCESSALGO_H
#define PROCESSALGO_H
#include <windows.h>
#include <Gdiplus.h>
?
namespace nsimgtk
{
template <typename pixelType, Gdiplus::PixelFormat pixelFormat, class Processor>
bool ProcessPixelsOneByOne(Gdiplus::Bitmap* const p_bitmap, Processor processor, unsigned int x, unsigned int y,
unsigned int width, unsigned int height)
{
if (p_bitmap == NULL)
{
return false;
}
if ((width + x > p_bitmap->GetWidth()) || (height + y >p_bitmap->GetHeight()))
{
return false;
}
Gdiplus::BitmapData bitmapData;
Gdiplus::Rect rect(x, y, width,height);
if (p_bitmap->LockBits(&rect, Gdiplus::ImageLockModeWrite, pixelFormat, &bitmapData) != Gdiplus::Ok)
{
return false;
}
pixelType *pixels = (pixelType*)bitmapData.Scan0;
?
for (unsigned int row=0; row<height; ++row)
{
for (unsigned int col=0; col<width; ++col)
{
processor(&pixels[col+row*bitmapData.Stride/sizeof(pixelType)]);
}
}
if (p_bitmap->UnlockBits(&bitmapData) != Gdiplus::Ok)
{
return false;
}
return true;
}
}
#endif
?
ProcessPixelsOneByOne函數可以對圖像中從(x,y)位置起始,width*height大小的區域進行處理。模板參數pixelType用于指定像素大小,例如在Win32平臺上傳入unsigned char即為8位,用于8階灰度圖。模板參數Processor為圖像處理算法實現,可以定義類實現void operator(pixelType *)函數,或者傳入同樣接口的函數指針。
如下便是一些算法示例(說明見具體注釋):
#ifndef SPATIALDOMAIN_H
#define SPATIALDOMAIN_H
#include <cmath>
#include <string>
namespace nsimgtk
{
// 8階灰度圖的灰度反轉算法
class NegativeGray8
{
public:
void operator()(unsigned char *const p_value)
{
*p_value ^= 0xff;
}
};
// 8階灰度圖的Gamma校正算法
class GammaCorrectGray8
{
private:
unsigned char d_s[256];
public:
GammaCorrectGray8::GammaCorrectGray8(double c, double gamma);
void operator()(unsigned char*const p_value)
{
*p_value = d_s[*p_value];
}
};
// 8階灰度圖的飽和度拉伸算法
class ContrastStretchingGray8
{
private:
unsigned char d_s[256];
public:
ContrastStretchingGray8::ContrastStretchingGray8(double a1, double b1, unsigned int x1,
double a2, double b2, unsigned int x2, double a3, double b3);
void operator()(unsigned char*const p_value)
{
*p_value = d_s[*p_value];
}
};
// 8階灰度圖的位平面分割,構造函數指定位平面號
class BitPlaneSliceGray8
{
private:
unsigned char d_s[256];
public:
BitPlaneSliceGray8(unsigned char bitPlaneNum);
void operator()(unsigned char* const p_value)
{
*p_value = d_s[*p_value];
}
};
}
#endif
// 上述類中各構造函數的實現代碼,應該分在另一個文件中,此處為說明方便,一并列出
#include "SpatialDomain/spatialDomain.h"
namespace nsimgtk
{
GammaCorrectGray8::GammaCorrectGray8(double c, double gamma)
{
double temp;
for (unsigned int i=0; i<256; ++i)
{
temp = ceil(c * 255.0 * pow(double(i)/255.0, gamma));
d_s[i] = unsigned char(temp);
}
}
ContrastStretchingGray8::ContrastStretchingGray8(double a1, double b1, unsigned int x1,
double a2, double b2, unsigned int x2, double a3, double b3)
{
if (x1 > 255 || x2 > 255 || x1 > x1)
{
for (unsigned int i=0; i<256; ++i)
d_s[i] = i;
}
else
{
double tmp;
for (unsigned int i=0; i<x1; ++i)
{
tmp = ceil(a1*double(i)+b1);
d_s[i] = (unsigned char)tmp;
}
for (unsigned int i=x1; i<x2; ++i)
{
tmp = ceil(a2*double(i)+b2);
d_s[i] = (unsigned char)tmp;
}
for (unsigned int i=x2; i<256; ++i)
{
tmp = ceil(a3*double(i)+b3);
d_s[i] = (unsigned char)tmp;
}
}
}
BitPlaneSliceGray8::BitPlaneSliceGray8(unsigned char bitPlaneNum)
{
unsigned char bitMaskArray[8] =
{
0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80
};
for (unsigned int i=0; i<256; ++i)
{
unsigned char tmp = i;
tmp &= bitMaskArray[bitPlaneNum];
tmp = (tmp >> bitPlaneNum) * 255;
d_s[i] = tmp;
}
}
}
?
(2) 直方圖在GDI+1.0中沒有獲得支持,我們必須自行實現。直方圖相關的處理在數字圖像處理中占有重要地位,可以通過它獲取圖像灰度級的統計信息,且可以通過直方圖進行一些重要的圖像增強技術,如直方圖均衡化,直方圖規定化,基本全局門限等。
下面是獲取8階圖像直方圖的算法實現:
namespace nsimgtk
{
bool GetHistogramNormalizeGray8(Gdiplus::Bitmap * const p_bitmap, float *histogramArray)
{
if (p_bitmap == NULL || histogramArray == NULL)
{
return false;
}
Gdiplus::BitmapData bitmapData;
Gdiplus::Rect rect(0, 0, p_bitmap->GetWidth(), p_bitmap->GetHeight());
if (p_bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat8bppIndexed, &bitmapData) != Gdiplus::Ok)
{
return false;
}
unsigned char *pixels = (unsigned char*)bitmapData.Scan0;
unsigned int histogram[256];
for (int i=0; i<256; ++i)
{
histogram[i] = 0;
}
for (unsigned int row=0; row<p_bitmap->GetWidth(); ++row)
{
for (unsigned int col=0; col<p_bitmap->GetHeight(); ++col)
{
++histogram[pixels[col+row*bitmapData.Stride]];
}
}
const unsigned int totalPixels = p_bitmap->GetWidth() * p_bitmap->GetHeight();
for (int i=0; i<256; ++i)
{
histogramArray[i] = float(histogram[i]) / float(totalPixels);
}
if (p_bitmap->UnlockBits(&bitmapData) != Gdiplus::Ok)
{
return false;
}
return true;
}
}
?
在獲取直方圖后(即上面算法的第二個參數),再將其作為參數傳入下面的對象的構造函數,然后以該對象為仿函數傳入ProcessPixelsOneByOne即可實現8階圖像直方圖均衡化:
#ifndef SPATIALDOMAIN_H
#define SPATIALDOMAIN_H
#include <cmath>
#include <string>
namespace nsimgtk
{
// 8階灰度圖的直方圖均衡化
class HistogramEqualizationGray8
{
private:
unsigned char d_s[256];
public:
HistogramEqualizationGray8(const float *const histogramArray);
void operator()(unsigned char *const p_value)
{
*p_value = d_s[*p_value];
}
};
}
#endif
//
#include "SpatialDomain/spatialDomain.h"
namespace nsimgtk
{
HistogramEqualizationGray8::HistogramEqualizationGray8(const float *const histogramArray)
{
if (histogramArray != NULL)
{
float sum = 0.0;
for (int i=0; i<256; ++i)
{
sum += histogramArray[i];
d_s[i] = unsigned char(sum * 255);
}
}
}
}
?
(3)空間域濾波器,濾波器是一個m*n大小的掩模,其中m,n均為大于1的奇數。濾波器逐像素地通過圖像的全部或部分矩形區域,然后逐像素地對掩模覆蓋下的像素使用濾波器算法獲得響應,將響應賦值于當前像素即掩模中心像素,另外濾波器算法使用中將會涉及到圖像邊緣的問題,這可以對邊緣部分掩模使用補零法補齊掩模下無像素值的區域,或者掩模的移動范圍以不越出圖像邊緣的方式移動,當然這些處理方法都會給圖像邊緣部分帶來不良效果,但是一般情況下,圖像邊緣部分往往不是我們關注的部分或者沒有重要的信息。
下面的濾波器算法框架SpatialFilterAlgo即以補零法(zero-padding)實現:
#ifndef SPATIALFILTER_H
#define SPATIALFILTER_H
#include <vector>
#include <numeric>
#include <algorithm>
#include <gdiplus.h>
#include <fstream>
#include <cmath>
namespace nsimgtk
{
template <typename pixelType, Gdiplus::PixelFormat pixelFormat, class FilterMask>
bool SpatialFilterAlgo(Gdiplus::Bitmap* const p_bitmap, FilterMask filterMask, unsigned int x, unsigned int y,
unsigned int width, unsigned int height)
{
if (p_bitmap == NULL)
{
return false;
}
if ((width + x > p_bitmap->GetWidth()) || (height + y >p_bitmap->GetHeight()))
{
return false;
}
Gdiplus::BitmapData bitmapData;
Gdiplus::Rect rect(x, y, width,height);
if (p_bitmap->LockBits(&rect, Gdiplus::ImageLockModeWrite, pixelFormat, &bitmapData) != Gdiplus::Ok)
{
return false;
}
pixelType *pixels = (pixelType*)bitmapData.Scan0;
const unsigned int m = filterMask.d_m; // mask's width
const unsigned int n = filterMask.d_n; // mask's height
std::vector<pixelType> tmpImage((m-1+width)*(n-1+height)); // extend image to use zero-padding
// copy original bitmap to extended image with zero-padding method
for (unsigned int row=0; row<height; ++row)
{
for (unsigned int col=0; col<width; ++col)
{
tmpImage[(col+m/2)+(row+n/2)*(bitmapData.Stride/sizeof(pixelType)+m-1)] =
pixels[col+row*bitmapData.Stride/sizeof(pixelType)];
}
}
// process every pixel with filterMask
for (unsigned int row=0; row<height; ++row)
{
for (unsigned int col=0; col<width; ++col)
{
// fill the "m*n" mask with the current pixel's neighborhood
for (unsigned int i=0; i<n; ++i)
{
for (unsigned int j=0; j<m; ++j)
{
filterMask.d_mask[i*m+j] = tmpImage[(col+j)+(row+i)*(bitmapData.Stride/sizeof(pixelType)+m-1)];
}
}
// replace the current pixel with filter mask's response
pixels[col+row*bitmapData.Stride/sizeof(pixelType)] = filterMask.response();
}
}
if (p_bitmap->UnlockBits(&bitmapData) != Gdiplus::Ok)
{
return false;
}
return true;
}
}
#endif
?
其中模板參數FilterMask即為濾波掩模算法。通常的濾波算法有均值濾波器,可以模糊化圖像,去除圖形中的細節部分,使得我們可以關注圖像中較為明顯的部分,均值濾波器用于周期性噪聲。中值濾波器用于圖像中存在椒鹽噪聲也即脈沖噪聲的情況下。另外有基于一階微分的Sobel梯度算子和基于兩階微分的拉普拉斯算子,它們往往被用于邊緣檢測中。
下面是一些濾波器算法的具體實現,所以濾波器算法都應該實現pixelType response()函數以及有d_mask,d_m,d_n成員,這可以通過繼承__filteMask類獲得(不需要付出虛函數代價)。
#ifndef SPATIALFILTER_H
#define SPATIALFILTER_H
#include <vector>
#include <numeric>
#include <algorithm>
#include <gdiplus.h>
#include <fstream>
#include <cmath>
namespace nsimgtk
{
// 濾波器掩模的基類,提供掩模大小d_m,d_n,掩模覆蓋下的m*n個像素值d_mask
// others filterMask should inherit it
template <typename pixelType>
struct __filterMask
{
const unsigned int d_m;
const unsigned int d_n;
// image's pixels under the m*n filter mask
std::vector<pixelType> d_mask;
// filter mask's width and heigh must be a odd, if not, it will plus one for the width or the height
__filterMask(unsigned int m, unsigned int n)
: d_m(m%2 ? m:m+1), d_n(n%2 ? n:n+1), d_mask(d_m*d_n)
{
}
};
// 掩模權值為全1的均值濾波器
template <typename pixelType>
class averagingFilterMaskSp
: public __filterMask<pixelType>
{
public:
averagingFilterMaskSp(unsigned int m, unsigned int n)
: __filterMask<pixelType>(m, n)
{ }
pixelType response()
{
return std::accumulate(d_mask.begin(), d_mask.end(), 0) / (d_m * d_n);
}
};
// 可自定義掩模權值的均值濾波器
template <typename pixelType>
class averagingFilterMask
: public __filterMask<pixelType>
{
private:
std::vector<pixelType> d_weight; // weights' vector(m*n)
int d_weight_sum; // all weights' sum
public:
averagingFilterMask(unsigned int m, unsigned int n, const std::vector<pixelType>& weightVec)
: __filterMask<pixelType>(m, n), d_weight(weightVec)
{
if (weightVec.size() != d_mask.size())
{
// if weight's size isn't equal to mask's size, it will change filter mask as a special filter mask
d_weight.resize(d_mask.size(), 1);
}
d_weight_sum = std::accumulate(d_weight.begin(), d_weight.end(), 0);
}
pixelType response()
{
return std::inner_product(d_mask.begin(), d_mask.end(), d_weight.begin(), 0) / d_weight_sum;
}
};
// 中值濾波器
template <typename pixelType>
class medianFilterMask
: public __filterMask<pixelType>
{
public:
medianFilterMask(unsigned int m, unsigned int n)
: __filterMask<pixelType>(m, n)
{ }
pixelType response()
{
std::sort(d_mask.begin(), d_mask.end());
return d_mask[d_mask.size()/2];
}
};
// 3*3拉普拉斯濾波器
// the mask is: [0 1 0 [0 -1 0
// 1 -5 1 or -1 5 -1
// 0 1 0] 0 -1 0]
// if pixel's brightness is less than min, set it to min
// if pixel's brightness is larger than max, set it to max
template <typename pixelType, pixelType min, pixelType max>
class laplacianFilter
: public __filterMask<pixelType>
{
public:
laplacianFilter()
: __filterMask<pixelType>(3, 3)
{ }
pixelType response()
{
int ret = (int)(5*(int)d_mask[4]) - ((int)d_mask[5]+d_mask[3]+d_mask[1]+d_mask[7]);
if (ret < min)
ret = min;
if (ret > max)
ret = max;
return ret;
}
};
// 3*3Sobel濾波器
// the mask is: [-1 -2 -1 [-1 0 1
// 0 0 0 and -2 0 2
// 1 2 1] -1 0 1]
// if pixel's brightness is larger than max, set it to max
template <typename pixelType, pixelType max>
class sobelFilter
: public __filterMask<pixelType>
{
public:
sobelFilter()
: __filterMask<pixelType>(3, 3)
{ }
pixelType response()
{
int ret = ::abs(d_mask[6]+2*d_mask[7]+d_mask[8]-d_mask[0]-2*d_mask[1]-d_mask[2])
+ ::abs(d_mask[2]+2*d_mask[5]+d_mask[8]-d_mask[0]-2*d_mask[3]-d_mask[6]);
if (ret > max)
ret = max;
return ret;
}
};
}
#endif
轉載于:https://www.cnblogs.com/songtzu/archive/2013/01/05/2845072.html
總結
以上是生活随笔為你收集整理的GDI+有Bitmap类。的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 结构体的传参理解成员的存储方式
- 下一篇: Exception Error log