日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

GDI+有Bitmap类。

發布時間:2023/12/18 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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类。的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。