在Boost中,有一个图像处理库,那就是GIL,该库是由鼎鼎大名的Adobe公司贡献的,它在图像处理方面的优势可想而知。使用GIL,可以非常方便地对图像数据进行处理而不用考虑太多的细节,比如很简单地定位图像的行、列或任意像素,可以任意定位图像中的某一个颜色通道,甚至提取其中一个通道构建另外一个灰度图像,可以方便地将RGB图像转换成RGBA、灰度图像或者反过来转换,可以方便地旋转、倒置图像......优点实在是太多,我也说不好。当然,最重要的优点是它的通用性和性能。
但是使用Boost.GIL有一个很大的障碍,那就是它的IO扩展需要编译第三方的图像解码库做支持,而且这个编译过程很麻烦。作为一个跨平台的库,这么做无可厚非,但是在Windows平台上,就没有必要使用第三方的图像解码库了,难道Windows提供的图像解码功能还不够强吗?
在Visual C++中,有一个类CImage可以读取任意格式的图像文件,无论bmp、gif、tiff、jpeg还是png,都行。还可以将图像数据保存到文件,还可以非常方便地将图像显示到屏幕上。下面,我将使用MFC实现一个简单的SDI程序,在该程序的基础上,可以非常方便第添加功能,使用GIL对图像数据进行操作。
第一步,使用MFC向导创建一个SDI程序。
第二步,在文档类中添加几个变量,用来保存图像数据,image_src保存源图像数据,image_dst保存修改后的图像数据,同时定义两个view,用到了GIL中的类型,可以看到,我都是使用的RGBA模式的图像数据。代码如下:
第三步,重写文档类的OnOpenDocument()方法。在这个方法中,使用CImage类读取图像文件,然后使用GIL将图像文件的数据复制到image_src和image_dst中。不管原始图像是RGB还是RGBA的,最后统一都变成了RGBA的。代码如下:
第四步,重现MFC视图类中的OnDraw()方法,在窗口中显示image_dst,代码如下:
至此,简单的框架搭建完成。如果要对图像数据进行操作,只需要操作view_src,然后将结果写入view_dst即可。比如,要使用GIL自带的例子中的x_gradient算法,只需要以下两个步骤:
1、将GIL例子中的几个模板函数代码复制到我们的项目中,如下:
2、为我们的SDI程序添加一个菜单,并在菜单的处理函数中调用上面的函数,如下:
运行程序,点击这个菜单,就可以看到效果了。
但是使用Boost.GIL有一个很大的障碍,那就是它的IO扩展需要编译第三方的图像解码库做支持,而且这个编译过程很麻烦。作为一个跨平台的库,这么做无可厚非,但是在Windows平台上,就没有必要使用第三方的图像解码库了,难道Windows提供的图像解码功能还不够强吗?
在Visual C++中,有一个类CImage可以读取任意格式的图像文件,无论bmp、gif、tiff、jpeg还是png,都行。还可以将图像数据保存到文件,还可以非常方便地将图像显示到屏幕上。下面,我将使用MFC实现一个简单的SDI程序,在该程序的基础上,可以非常方便第添加功能,使用GIL对图像数据进行操作。
第一步,使用MFC向导创建一个SDI程序。
第二步,在文档类中添加几个变量,用来保存图像数据,image_src保存源图像数据,image_dst保存修改后的图像数据,同时定义两个view,用到了GIL中的类型,可以看到,我都是使用的RGBA模式的图像数据。代码如下:
CImage image_src;
rgba8_view_t view_src;
CImage image_dst;
rgba8_view_t view_dst;
rgba8_view_t view_src;
CImage image_dst;
rgba8_view_t view_dst;
第三步,重写文档类的OnOpenDocument()方法。在这个方法中,使用CImage类读取图像文件,然后使用GIL将图像文件的数据复制到image_src和image_dst中。不管原始图像是RGB还是RGBA的,最后统一都变成了RGBA的。代码如下:
BOOL CGIL_StudyDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
if ( ! CDocument::OnOpenDocument(lpszPathName))
return FALSE;
CImage loader;
loader.Load(lpszPathName);
if (loader.IsNull())
return FALSE;
unsigned char * buffer_loader = (unsigned char * )loader.GetBits() + (loader.GetPitch() * (loader.GetHeight() - 1 ));
if ( ! image_src.IsNull())image_src.Destroy();
image_src.Create(loader.GetWidth(),loader.GetHeight(), 32 );
unsigned char * buffer_src = (unsigned char * )image_src.GetBits() + (image_src.GetPitch() * (image_src.GetHeight() - 1 ));
view_src = interleaved_view(image_src.GetWidth(),image_src.GetHeight(),(rgba8_pixel_t * )buffer_src,image_src.GetWidth() * 4 );
if ( ! image_dst.IsNull())image_dst.Destroy();
image_dst.Create(loader.GetWidth(),loader.GetHeight(), 32 );
unsigned char * buffer_dst = (unsigned char * )image_dst.GetBits() + (image_dst.GetPitch() * (image_dst.GetHeight() - 1 ));
view_dst = interleaved_view(image_dst.GetWidth(),image_dst.GetHeight(),(rgba8_pixel_t * )buffer_dst,image_dst.GetWidth() * 4 );
switch (loader.GetBPP()){
case 24 :
{
rgb8_view_t view_loader = interleaved_view(
loader.GetWidth(),loader.GetHeight(),
(rgb8_pixel_t * )buffer_loader,std::abs(loader.GetPitch()));
copy_pixels(
color_converted_view < rgba8_image_t::value_type > (view_loader),
view_src);
copy_pixels(
color_converted_view < rgba8_image_t::value_type > (view_loader),
view_dst);
break ;
}
case 32 :
{
rgba8_view_t view_loader = interleaved_view(
loader.GetWidth(),loader.GetHeight(),
(rgba8_pixel_t * )buffer_loader,loader.GetWidth() * 4 );
copy_pixels(
color_converted_view < rgba8_image_t::value_type > (view_loader),
view_src);
copy_pixels(
color_converted_view < rgba8_image_t::value_type > (view_loader),
view_dst);
break ;
}
default :
assert( false );
break ;
}
return TRUE;
}
{
if ( ! CDocument::OnOpenDocument(lpszPathName))
return FALSE;
CImage loader;
loader.Load(lpszPathName);
if (loader.IsNull())
return FALSE;
unsigned char * buffer_loader = (unsigned char * )loader.GetBits() + (loader.GetPitch() * (loader.GetHeight() - 1 ));
if ( ! image_src.IsNull())image_src.Destroy();
image_src.Create(loader.GetWidth(),loader.GetHeight(), 32 );
unsigned char * buffer_src = (unsigned char * )image_src.GetBits() + (image_src.GetPitch() * (image_src.GetHeight() - 1 ));
view_src = interleaved_view(image_src.GetWidth(),image_src.GetHeight(),(rgba8_pixel_t * )buffer_src,image_src.GetWidth() * 4 );
if ( ! image_dst.IsNull())image_dst.Destroy();
image_dst.Create(loader.GetWidth(),loader.GetHeight(), 32 );
unsigned char * buffer_dst = (unsigned char * )image_dst.GetBits() + (image_dst.GetPitch() * (image_dst.GetHeight() - 1 ));
view_dst = interleaved_view(image_dst.GetWidth(),image_dst.GetHeight(),(rgba8_pixel_t * )buffer_dst,image_dst.GetWidth() * 4 );
switch (loader.GetBPP()){
case 24 :
{
rgb8_view_t view_loader = interleaved_view(
loader.GetWidth(),loader.GetHeight(),
(rgb8_pixel_t * )buffer_loader,std::abs(loader.GetPitch()));
copy_pixels(
color_converted_view < rgba8_image_t::value_type > (view_loader),
view_src);
copy_pixels(
color_converted_view < rgba8_image_t::value_type > (view_loader),
view_dst);
break ;
}
case 32 :
{
rgba8_view_t view_loader = interleaved_view(
loader.GetWidth(),loader.GetHeight(),
(rgba8_pixel_t * )buffer_loader,loader.GetWidth() * 4 );
copy_pixels(
color_converted_view < rgba8_image_t::value_type > (view_loader),
view_src);
copy_pixels(
color_converted_view < rgba8_image_t::value_type > (view_loader),
view_dst);
break ;
}
default :
assert( false );
break ;
}
return TRUE;
}
第四步,重现MFC视图类中的OnDraw()方法,在窗口中显示image_dst,代码如下:
void
CGIL_StudyView::OnDraw(CDC
*
pDC)
{
CGIL_StudyDoc * pDoc = GetDocument();
ASSERT_VALID(pDoc);
if ( ! pDoc)
return ;
if (pDoc -> image_dst.IsNull()) return ;
pDoc -> image_dst.Draw( * pDC, 0 , 0 ,pDoc -> image_dst.GetWidth(),pDoc -> image_dst.GetHeight());
}
{
CGIL_StudyDoc * pDoc = GetDocument();
ASSERT_VALID(pDoc);
if ( ! pDoc)
return ;
if (pDoc -> image_dst.IsNull()) return ;
pDoc -> image_dst.Draw( * pDC, 0 , 0 ,pDoc -> image_dst.GetWidth(),pDoc -> image_dst.GetHeight());
}
至此,简单的框架搭建完成。如果要对图像数据进行操作,只需要操作view_src,然后将结果写入view_dst即可。比如,要使用GIL自带的例子中的x_gradient算法,只需要以下两个步骤:
1、将GIL例子中的几个模板函数代码复制到我们的项目中,如下:
template
<
typename SrcView, typename DstView
>
void x_gradient( const SrcView & src, const DstView & dst) {
for ( int y = 0 ; y < src.height(); ++ y) {
typename SrcView::x_iterator src_it = src.row_begin(y);
typename DstView::x_iterator dst_it = dst.row_begin(y);
for ( int x = 1 ; x < src.width() - 1 ; ++ x) {
for ( int c = 0 ; c < num_channels < SrcView > ::value; ++ c)
dst_it[x][c] = (src_it[x - 1 ][c] - src_it[x + 1 ][c]) / 2 ;
}
}
}
void x_gradient( const SrcView & src, const DstView & dst) {
for ( int y = 0 ; y < src.height(); ++ y) {
typename SrcView::x_iterator src_it = src.row_begin(y);
typename DstView::x_iterator dst_it = dst.row_begin(y);
for ( int x = 1 ; x < src.width() - 1 ; ++ x) {
for ( int c = 0 ; c < num_channels < SrcView > ::value; ++ c)
dst_it[x][c] = (src_it[x - 1 ][c] - src_it[x + 1 ][c]) / 2 ;
}
}
}
2、为我们的SDI程序添加一个菜单,并在菜单的处理函数中调用上面的函数,如下:
void
CGIL_StudyView::OnXGradient()
{
CGIL_StudyDoc * pDoc = GetDocument();
ASSERT_VALID(pDoc);
if ( ! pDoc)
return ;
x_gradient(pDoc -> view_src,pDoc -> view_dst);
Invalidate();
}
{
CGIL_StudyDoc * pDoc = GetDocument();
ASSERT_VALID(pDoc);
if ( ! pDoc)
return ;
x_gradient(pDoc -> view_src,pDoc -> view_dst);
Invalidate();
}
运行程序,点击这个菜单,就可以看到效果了。