使用GDI+位图数据扫描线处理图像的小技巧 【转】from http://blog.csdn.net/maozefa/article/details/4533770
2013-01-25 14:51
435 查看
在GDI+图像处理中,我们经常利用BitmapData结构对图像数据扫描线进行操作,在我的大部分BOLG文章中,都使用了这个方法。GDI+位图通过其LockBits方法和UnlockBits方法,分别用来锁定(获取)和解锁(释放)BitmapData数据,我们一般都在这2个方法之间操作图像数据扫描线,如:
[cpp] view plaincopyprint?
Bitmap *bmp = new Bitmap(L"d://001-1.jpg");
BitmapData data;
Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
// 锁定为32位像素格式
bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite, PixelFormat32bppARGB, &data);
// 这里对图像数据扫描线操作,由于bmp已经锁定,很多方法不能调用
bmp->UnlockBits(&data); // 解锁
Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
g->DrawImage(bmp, 0, 0);
delete g;
delete bmp;
由于在LockBits方法和UnlockBits方法之间,位图对象是锁定的,很多方法无法调用,有时也感到有些不方便,甚至繁琐。比如对图像数据扫描线进行多次处理,在处理过程中想分步骤显示或者保存时,就不得不反复调用这2个方法;还有就是位图格式低于24位格式的图像无法锁定为24位或32位数据进行操作(我们大多利用24位或者32位像素扫描线进行图像处理)等等。
可以使用一些小技巧来规避因位图对象锁定而带来的不方便,也可对低于24位格式的图像进行24位或32位图像数据扫描线操作。请看下面的例子:
[cpp] view plaincopyprint?
Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
Bitmap *bmp = new Bitmap(L"d://001-1.jpg");
BitmapData data;
Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
// 预先设置扫描线长度和图像数据
data.Stride = r.Width * 4;
data.Scan0 = (void*)new char[r.Height * data.Stride];
// 建立一个32位像素格式的自定义数据位图对象
Bitmap *bmp2 = new Bitmap(r.Width, r.Height, data.Stride,
PixelFormat32bppARGB, (BYTE*)data.Scan0);
// 使用ImageLockModeRead标记使bmp图像数据拷贝到data.Scan0。
// 使用ImageLockModeUserInputBuf标记锁定图像bmp,使bmp和bmp2共享图像数据。
// 使用ImageLockModeWrite标记使bmp和bmp2同步,即图像数据处理同时
// 作用于bmp和bmp2,去掉ImageLockModeWrite后,bmp图像不改变,
// 低于24位格式的图像必须去掉ImageLockModeWrite
bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite | ImageLockModeUserInputBuf,
PixelFormat32bppARGB, &data);
// 这里可分步操作图像扫描线,同时也可显示bmp2,或者调用bmp2的任何方法
ImageGray(&data); // 图像灰度化,具体过程略
g->DrawImage(bmp2, 0, 0); // 画图像(或者其它方法调用)
ImageTwoValues(&dada, 127); // 图像二值化,具体过程略
g->DrawImage(bmp2, 200, 0); // 画图像(或者其它方法调用)
bmp->UnlockBits(&data);
delete g;
delete bmp;
delete bmp2;
delete[] data.Scan0; // 必须释放
上面例子代码中作了较详细的说明,就不再解释。
上面的例子为了解释位图对象共享和数据处理同步,代码显得有些凌乱,其实只要记住一点:例子中,自定义位图对象bmp2通过bmp->LockBits方法取得数据后,如果无特殊需要,bmp就可解锁甚至delete,这时也不再需要对bmp2锁定,就可通过对data的处理,达到对bmp2包含的图像数据进行改变的目的。
将上面代码重新规划一下,使之清晰一些:
[cpp] view plaincopyprint?
BOOL GetBitmapData(WCHAR *fileName, PixelFormat pixelFormat, BitmapData *data)
{
Bitmap *bmp = new Bitmap(fileName);
if (bmp->GetLastStatus() != Ok)
return FALSE;
Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
UINT pixelSize = GetPixelFormatSize(pixelFormat);
data->Stride = ((pixelSize * r.Width + 31) & 0xffffffe0) >> 3;
data->Scan0 = (void*)new char[r.Height * data->Stride];
bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf,
pixelFormat, data);
bmp->UnlockBits(data);
delete bmp;
return TRUE;
}
void __fastcall TForm2::Button1Click(TObject *Sender)
{
// 取得图像的24位像素格式数据
BitmapData data;
if (!GetBitmapData(L"d://001-1.jpg", PixelFormat24bppRGB, &data))
return;
// 建立一个24位像素格式的自定义数据位图对象
Bitmap *bmp = new Bitmap(data.Width, data.Height, data.Stride,
data.PixelFormat, (BYTE*)data.Scan0);
Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
// 这里可操作图像数据扫描线,不必再锁定bmp,同时也可显示bmp,或者调用bmp的任何方法
ImageGray(&data); // 图像灰度化,具体过程略
g->DrawImage(bmp, 0, 0); // 画图像(或者其它方法调用)
ImageTwoValues(&dada, 127); // 图像二值化,具体过程略
g->DrawImage(bmp, 200, 0); // 画图像(或者其它方法调用)
delete g;
delete bmp;
delete[] data.Scan0; // 必须释放
}
通过上面代码,就可以看出调用GetBitmapData后,所有的图像数据信息就已经包含在BitmapData结构中了,所以我们可以对这个数据结构进行任何的操作,而不再依赖任何GDI+对象,由此避免了本文前面所说的不方便。之所以又建立一个自定义数据位图对象,只是要借助它进行图像显示、保存等操作而已。
利用类似于前面的例子代码还可以进行拼图操作:
[cpp] view plaincopyprint?
Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
Bitmap *bmp = new Bitmap(L"d://001-1.jpg");
BitmapData data;
Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
// 预先设置扫描线长度和图像数据,扫描线为2个图像的宽度
data.Stride = r.Width * 2 * 4;
data.Scan0 = (void*)new char[r.Height * data.Stride];
// 拷贝图像到左边,注意这里只是读取数据,所以ImageLockModeWrite没使用
bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf,
PixelFormat32bppARGB, &data);
bmp->UnlockBits(&data);
// 拼合图像到右边,为简化,使用同一张图。
// 如果图像大小不同,只要r的尺寸一致(不能大于原图)
(char*)data.Scan0 += (r.Width * 4); // 扫描线地址向右移动
bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf,
PixelFormat32bppARGB, &data);
bmp->UnlockBits(&data);
(char*)data.Scan0 -= (r.Width * 4); // 还原扫描线地址
data.Width = r.Width * 2; // 重新设置拼图宽度
// 拼合图数据信息已经包含在data中了,可以继续对data进行任何的操作
// 建立一个自定义数据位图对象
Bitmap *bmp2 = new Bitmap(data.Width, data.Height, data.Stride,
data.PixelFormat, (BYTE*)data.Scan0);
// 显示拼合后的图像。
g->DrawImage(bmp2, 0, 0);
delete g;
delete bmp;
delete bmp2;
delete[] data.Scan0; // 必须释放
[cpp] view plaincopyprint?
Bitmap *bmp = new Bitmap(L"d://001-1.jpg");
BitmapData data;
Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
// 锁定为32位像素格式
bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite, PixelFormat32bppARGB, &data);
// 这里对图像数据扫描线操作,由于bmp已经锁定,很多方法不能调用
bmp->UnlockBits(&data); // 解锁
Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
g->DrawImage(bmp, 0, 0);
delete g;
delete bmp;
由于在LockBits方法和UnlockBits方法之间,位图对象是锁定的,很多方法无法调用,有时也感到有些不方便,甚至繁琐。比如对图像数据扫描线进行多次处理,在处理过程中想分步骤显示或者保存时,就不得不反复调用这2个方法;还有就是位图格式低于24位格式的图像无法锁定为24位或32位数据进行操作(我们大多利用24位或者32位像素扫描线进行图像处理)等等。
可以使用一些小技巧来规避因位图对象锁定而带来的不方便,也可对低于24位格式的图像进行24位或32位图像数据扫描线操作。请看下面的例子:
[cpp] view plaincopyprint?
Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
Bitmap *bmp = new Bitmap(L"d://001-1.jpg");
BitmapData data;
Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
// 预先设置扫描线长度和图像数据
data.Stride = r.Width * 4;
data.Scan0 = (void*)new char[r.Height * data.Stride];
// 建立一个32位像素格式的自定义数据位图对象
Bitmap *bmp2 = new Bitmap(r.Width, r.Height, data.Stride,
PixelFormat32bppARGB, (BYTE*)data.Scan0);
// 使用ImageLockModeRead标记使bmp图像数据拷贝到data.Scan0。
// 使用ImageLockModeUserInputBuf标记锁定图像bmp,使bmp和bmp2共享图像数据。
// 使用ImageLockModeWrite标记使bmp和bmp2同步,即图像数据处理同时
// 作用于bmp和bmp2,去掉ImageLockModeWrite后,bmp图像不改变,
// 低于24位格式的图像必须去掉ImageLockModeWrite
bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite | ImageLockModeUserInputBuf,
PixelFormat32bppARGB, &data);
// 这里可分步操作图像扫描线,同时也可显示bmp2,或者调用bmp2的任何方法
ImageGray(&data); // 图像灰度化,具体过程略
g->DrawImage(bmp2, 0, 0); // 画图像(或者其它方法调用)
ImageTwoValues(&dada, 127); // 图像二值化,具体过程略
g->DrawImage(bmp2, 200, 0); // 画图像(或者其它方法调用)
bmp->UnlockBits(&data);
delete g;
delete bmp;
delete bmp2;
delete[] data.Scan0; // 必须释放
上面例子代码中作了较详细的说明,就不再解释。
上面的例子为了解释位图对象共享和数据处理同步,代码显得有些凌乱,其实只要记住一点:例子中,自定义位图对象bmp2通过bmp->LockBits方法取得数据后,如果无特殊需要,bmp就可解锁甚至delete,这时也不再需要对bmp2锁定,就可通过对data的处理,达到对bmp2包含的图像数据进行改变的目的。
将上面代码重新规划一下,使之清晰一些:
[cpp] view plaincopyprint?
BOOL GetBitmapData(WCHAR *fileName, PixelFormat pixelFormat, BitmapData *data)
{
Bitmap *bmp = new Bitmap(fileName);
if (bmp->GetLastStatus() != Ok)
return FALSE;
Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
UINT pixelSize = GetPixelFormatSize(pixelFormat);
data->Stride = ((pixelSize * r.Width + 31) & 0xffffffe0) >> 3;
data->Scan0 = (void*)new char[r.Height * data->Stride];
bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf,
pixelFormat, data);
bmp->UnlockBits(data);
delete bmp;
return TRUE;
}
void __fastcall TForm2::Button1Click(TObject *Sender)
{
// 取得图像的24位像素格式数据
BitmapData data;
if (!GetBitmapData(L"d://001-1.jpg", PixelFormat24bppRGB, &data))
return;
// 建立一个24位像素格式的自定义数据位图对象
Bitmap *bmp = new Bitmap(data.Width, data.Height, data.Stride,
data.PixelFormat, (BYTE*)data.Scan0);
Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
// 这里可操作图像数据扫描线,不必再锁定bmp,同时也可显示bmp,或者调用bmp的任何方法
ImageGray(&data); // 图像灰度化,具体过程略
g->DrawImage(bmp, 0, 0); // 画图像(或者其它方法调用)
ImageTwoValues(&dada, 127); // 图像二值化,具体过程略
g->DrawImage(bmp, 200, 0); // 画图像(或者其它方法调用)
delete g;
delete bmp;
delete[] data.Scan0; // 必须释放
}
通过上面代码,就可以看出调用GetBitmapData后,所有的图像数据信息就已经包含在BitmapData结构中了,所以我们可以对这个数据结构进行任何的操作,而不再依赖任何GDI+对象,由此避免了本文前面所说的不方便。之所以又建立一个自定义数据位图对象,只是要借助它进行图像显示、保存等操作而已。
利用类似于前面的例子代码还可以进行拼图操作:
[cpp] view plaincopyprint?
Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
Bitmap *bmp = new Bitmap(L"d://001-1.jpg");
BitmapData data;
Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
// 预先设置扫描线长度和图像数据,扫描线为2个图像的宽度
data.Stride = r.Width * 2 * 4;
data.Scan0 = (void*)new char[r.Height * data.Stride];
// 拷贝图像到左边,注意这里只是读取数据,所以ImageLockModeWrite没使用
bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf,
PixelFormat32bppARGB, &data);
bmp->UnlockBits(&data);
// 拼合图像到右边,为简化,使用同一张图。
// 如果图像大小不同,只要r的尺寸一致(不能大于原图)
(char*)data.Scan0 += (r.Width * 4); // 扫描线地址向右移动
bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf,
PixelFormat32bppARGB, &data);
bmp->UnlockBits(&data);
(char*)data.Scan0 -= (r.Width * 4); // 还原扫描线地址
data.Width = r.Width * 2; // 重新设置拼图宽度
// 拼合图数据信息已经包含在data中了,可以继续对data进行任何的操作
// 建立一个自定义数据位图对象
Bitmap *bmp2 = new Bitmap(data.Width, data.Height, data.Stride,
data.PixelFormat, (BYTE*)data.Scan0);
// 显示拼合后的图像。
g->DrawImage(bmp2, 0, 0);
delete g;
delete bmp;
delete bmp2;
delete[] data.Scan0; // 必须释放
相关文章推荐
- 使用GDI+位图数据扫描线处理图像的小技巧
- 使用GDI+位图数据扫描线处理图像的小技巧
- 使用GDI+位图数据扫描线处理图像的小技巧 from http://blog.csdn.net/maozefa/article/details/4533770
- 使用GDI+位图数据扫描线处理图像的小技巧
- 使用gdal读取图像数据,然后用构建gdi+位图显示
- VC6使用GDI+进行图像的特效处理和MFC学习笔记-1
- 使用FlasCC处理位图数据
- 使用GDI+可以方便的把OpenCV的图像矩阵类型数据显示在MFC的窗口中
- 数字图像处理 CImage类的使用与封装(jpg png gif tif bmp等格式图像的加载、数据读写、保存等功能)
- 使用GDI+进行图像处理
- 最近在做的图形图像处理项目,使用GDI+(贴图)
- tensorflow处理自己的图像数据(不使用队列)
- numpy使用文件中的数据、图像处理等
- 使用Linux的V4L2读取摄像头数据+Opencv图像处理
- ImageGear for .NET扫描打印等图形图像处理控件介绍使用手册
- (转)在C#中使用WIA获取扫描仪数据(四、通过编程方式扫描图像)
- 使用GDI+处理数字图像
- 在ROS 使用摄像头 WebCam 完成图像处理(1) -- 获取图像数据
- 基因数据处理94之使用kmer分析SRR003161数据的kmer分布
- 使用 ibatis 处理复杂对象数据关系的实例