2007-10-12 17:14:00 kedoumy 阅读数 3155
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    29961 人正在学习 去看看 张中强
摘要: 关于空间域图像处理算法框架,直方图处理,空间域滤波器算法框架的编程心得,使用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

 
 
2004-12-27 09:33:00 newsuppy 阅读数 24763
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    29961 人正在学习 去看看 张中强

数字图像处理算法实现

                                 ------------编程心得(1)

2001414 朱伟 20014123

摘要: 关于空间域图像处理算法框架,直方图处理,空间域滤波器算法框架的编程心得,使用GDI+(C++)

一,图像文件的读取

    初学数字图像处理时,图像文件的读取往往是一件麻烦的事情,我们要面对各种各样的图像文件格式,如果仅用C++fstream库那就必须了解各种图像编码格式,这对于初学图像处理是不太现实的,需要一个能帮助轻松读取各类图像文件的库。在Win32平台上GDI+(C++)是不错的选择,不光使用上相对于Win32 GDI要容易得多,而且也容易移植到.Net平台上的GDI+

    Gdiplus::Bitmap类为我们提供了读取各类图像文件的接口,Bitmap::LockBits方法产生的BitmapData类也为我们提供了高速访问图像文件流的途径。这样我们就可以将精力集中于图像处理算法的实现,而不用关心各种图像编码。具体使用方式请参考MSDNGDI+文档中关于Bitmap类和BitmapData类的说明。另外GDI+仅在Windows XP/2003上获得直接支持,对于Windows 2000必须安装相关DLL,或者安装有Office 2003Visual Studio 2003 .Net等软件。

二,空间域图像处理算法框架

 (1) 在空间域图像处理中,对于一个图像我们往往需要对其逐个像素的进行处理,对每个像素的处理使用相同的算法(或者是图像中的某个矩形部分)。即,对于图像f(x,y),其中0xM,0yN,图像为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类也为我们提供了高速访问图像文件流的途径。这样我们就可以将精力集中于图像处理算法的实现,而不用关心各种图像编码。具体使用方式请参考MSDNGDI+文档中关于Bitmap类和BitmapData类的说明。另外GDI+仅在Windows XP/2003上获得直接支持,对于Windows 2000必须安装相关DLL,或者安装有Office 2003Visual Studio 2003 .Net等软件。

二,空间域图像处理算法框架

 (1) 在空间域图像处理中,对于一个图像我们往往需要对其逐个像素的进行处理,对每个像素的处理使用相同的算法(或者是图像中的某个矩形部分)。即,对于图像f(x,y),其中0xM,0yN,图像为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

 

2015-03-13 22:42:54 liuuze5 阅读数 13938
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    29961 人正在学习 去看看 张中强

整理了一下网上一些公司对图像处理算法工程师的招聘要求:

                                                            图像处理算法工程师

 

职位要求

编程技能:

1、 具有较强的编程能力和良好的编程习惯, 精通c/c++编程,并熟练使用VS 或matlab开发环境;

2、 在计算机技术领域拥有扎实的技术功底,尤其在数据结构、算法和代码、软件设计方面功力深厚;

    对数据结构有一定的研究基础如链表、堆杖、树等,熟悉数据库编程;

3、 出色的算法分析能力,对某一特定算法可以做广泛的综述,有实际算法实现经验;

4、 熟悉面向对象编程思想,精于windows下的C/C++、VC++程序设计,熟悉MATLAB,对MFC有相对的了解和应用经验;

专业技能:

1、扎实的数学功底和分析技能,精通计算机视觉中的数学方法;

     高等数学(微积分)、线性代数(矩阵论)、随机过程、概率论、

     摄影几何、模型估计、数理统计、张量代数、数据挖掘、数值分析等;

2、具备模式识别、图像处理、机器视觉、信号处理和人工智能等基础知识;

     对图像特征、机器学习有深刻认识与理解;

3、精通图像处理基本概念和常用算法包括图像预处理算法和高级处理算法;

     常见的图像处理算法,包括增强、分割、复原、形态学处理等; 

     熟悉常见的模式识别算法,特别是基于图像的模式识别算法,掌握特征提取、特征统计和分类器设计; 
4、熟练使用OpenCV、Matlab、Halcon中的一种或一种以上工具库;

5、熟悉机器视觉系统的硬体选型,包括CCD相机,镜头及光源;熟悉相机与镜头搭配;

 

外语:

1. 英文熟练,能够熟练阅读和理解专业英文资料,有英文文献检索和阅读能力;
2. 良好的英语沟通能力

综合能力:

1.对工作认真负责,积极主动,勤奋踏实;

2.做事严谨,注重细节,有耐心,能够在压力下独立工作;

3.学习钻研能力强,有较强的理解能力和逻辑思维能力和良好的创新意识;

4.良好的协调沟通能力和团队合作精神; 

经验要求:

1.两年以上C/C++ 程序设计经验;

2.具有2年以上在Linux/Unix环境下用C/C++语言开发图像处理软件的经验。

3.数字图像处理、模式识别的理论知识和实践经验;

  有基于OpenCV开发项目经验,机器视觉行业经验;

  具有图像处理算法设计和开发经验;

  参与过机器视觉系统分析和设计;

4. 在Matlab 或其它数学软件上开发算法的经验;

 

视觉算法经验:请提供实现的算法列表

    目标识别、图像配准、三维测量、标定和重建、手势识别; 

    表面缺陷检测;尺寸测量;特征识别;

    图像去噪、滤波、融合算法
    3A算法:如自动曝光、自动对焦、自动白平衡


【工作内容】: 


1.为解决实际问题而进行探索性研究和创新,设计与模式识别、图像/视频智能分析处理相关的算法。

  图形图像处理、计算机视觉相关算法的研发以及应用程序的编写;

  参与核心软件项目算法设计及算法实现;研究图像处理算法,开发和调试算法原型

  软件算法研发:算法的代码实现、优化以及移植及其测试;

  负责机器视觉系统图象处理、分析及识别算法的设计、实现及调试;

  参与图象算法视觉应用软件的设计与实现。参与图象处理技术研究与设计;

2、对已有的计算机视觉算法进行实用化开发和优化研究;
   精益求精,将算法做到极致,使算法真正实用化;

  参与预研性的算法分析和论证,为产品开发提供基础研究及论证;


岗位职责:

1、 协助工程师进行算法的测试.;C++语言验证、测试算法;

2、编写算法规格说明;
3、相关专业文献的查阅; 
4、将部分matlab程序转为C或C++语言程序。 

1) 辅助图像处理工作
2) 大规模图像搜集与分类 
3) 与开发人员等进行沟通,跟踪产品的体验效果并改进;
4)负责公司的机器视觉与传感器项目的技术支持(如项目可行性评估、现场DEMO、装机、培训等)

   和 维护工作;

 

岗位职责:

1、负责计算机视觉中的图像采集,处理面阵和线扫描相机的成像和控制 ;
2、针对特定的计算机视觉问题,设计目标识别与快速定位与检测算法的实现,并进行优化;
3、对彩色图像和灰度图像实现物体表面的污点划痕检测算法设计和实现;
4、处理三维物体表面数据获取和实现三维测量算法的实现;
5、处理点激光和线激光源的成像,散斑噪声滤波和轮廓检测;
6、负责算法与软件GUI开发工程师接口;
7、完成上级领导交办的其他的工作。




 

图像算法工程师三重境界



一、传统图像算法工程师: 
主要涉及图形处理,包括形态学、图像质量、相机成像之3A算法、去雾处理、颜色空间转换、滤镜等,主要在安防公司或者机器视觉领域,包括缺陷检测;

二、现代图像算法工程师: 
涉及模式识别,主要表现的经验为AdaboostSVM的研究与应用,特征选取与提取,包括智能驾驶的研究与应用、行人检测、人脸识别;

三、人工智能时代图像算法工程师: 
深度学习,主要在大型互联网公司或者研究所机构,具体体现在TensorFlow等开源库的研究与应用,包括机器人的研、基于深度学习的人脸识别;







2015-10-06 18:37:10 jaych 阅读数 2289
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    29961 人正在学习 去看看 张中强

最近刚接触图像算法开发,而且也是手机端的图像算法开发,一上来导师就给说说图像算法开发的过程。

图像算法开发跟普通程序的软件算法开发又不一样,图像算法主要是关注如何对图像进行处理,也就是关注的优化的对象不一样。

图像算法开发过程,一般是:
1)在PC端进行算法验证,达到所要实现的功能。因为在PC端能够更好的进行算法调试,确保功能实现。
2)PC端的优化,将算法优化到最佳状态。
3)算法移植到手机平台上,实现算法功能。
4)移动端算法优化,进行C/C++代码优化,NEON优化等等。后续将对如何进行代码优化进行总结。

在整个过程中,对算法的理解十分重要,而如何进行代码优化,NEON优化是算法实现的难点。

后续通过各种算法的实践、联系、总结,不断的加深理解。

2017-07-09 21:23:39 u010455056 阅读数 355
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    29961 人正在学习 去看看 张中强

OpenCV图像处理算法——初窥(一)

简介

本人也是新兵,一边学一边写,所以称为一篇入门笔记更恰当。但我觉得起个更能突出数学在编程中的重要性的名字也不是不可以,(绝非标题党)。这是一篇OpenCV描述基本图像处理算法的系列文章,主要涉及矩阵知识(推荐可汗学院线性代数公开课 https://www.khanacademy.org/math/linear-algebra)。不是文档的翻译,官方文档教程教程中能查到的术语示例,本文将不再赘述。
在下学习方向前端开发(数据可视化,web动画等),希望觅得同道知音一起拼搏江湖。打算一周写一篇,保证质量,初次写系列文章,难免会出错,期待能一起探讨学习 (邮箱 dferic@foxmail.com)。

准备

环境:OpenCV-3.2.0 + Visual Studio-2015 环境配置参考文档 http://docs.opencv.org/
配置完成后一个简单的示例来了解OpenCV中图像-矩阵关系,主要涉及Mat类,文档上有详尽介绍。

#include "stdafx.h"
#include "opencv2/imgcodecs.hpp"
#include <opencv2/highgui.hpp>
#include <iostream>

using namespace cv;

int main(void)
{
    Mat src1,src2,dst;
    src1 = imread("source.png",-1);//读入带alpha的png图片
    if (src1.empty()) { std::cout << "error load src1" << std::endl; return -1; };
    src2 = Mat(5, 5, CV_8UC4, Scalar(0, 0, 255,100));//用Mat构造函数创建一个5*5的Mat类
    dst = src1 + src2;//矩阵相加
    std::cout << "src1:" << src1 << std::endl;
    std::cout << "src2" <<src2 << std::endl;
    std::cout << "dst" <<dst << std::endl;
    imwrite("target.png", dst);//写入文件
    imshow("",dst);
    waitKey(0);
    return 0;
}

运行结果:
demo1

  1. 有不理解的函数希望能看看文档,或者直接转到声明-
  2. 分别以图像读入和Mat构造方法创建了两个mat类,存储的就是描述图像的矩阵,简单相加之后,得到新的mat类,可直接写入为图像,所以对图像的处理也就转换成对矩阵的操作

addWeighted函数

一般是混合两张图片用的,我觉得翻译为加权处理更适合,函数结构如下:

g(x)=(1α)f0(x)+αf1(x)

具体操作:

Mat src1,src2,dst;
    double alpha = 0.5;
    double beta = 1.0 - alpha;
    src1 = imread("source.png",-1);
    if (src1.empty()) { std::cout << "error load src1" << std::endl; return -1; };
    src2 = Mat(5, 5, CV_8UC4, Scalar(0, 0, 255,100));
    addWeighted(src1,alpha,src2,beta,0.0,dst);
    std::cout << "src1:" << std::endl << src1 << std::endl;
    std::cout << "src2" << std::endl << src2 << std::endl;
    std::cout << "dst" << std::endl << dst << std::endl;
    imwrite("target.png", dst);

结果:
demo2

这个OpenCV函数更直观描述了矩阵操作与图像图例之间关系,更复杂的图像操作就是矩阵的更复杂运算。

addWeighted(src1,alpha,src2,beta,0.0dst);

等价于
dst=alphasrc1+betasrc2+0.0;

结语

很后悔大学没有把线性代数这门课好好学,那时候真的不知道,也没人能让我知道数学和编程真的很紧密。如果有位老师能让我明白这门课在编程中重要性,我想开这门课的目的就达到一大半了。。。扯远了,如果忘了线代,找些资料重新学习学习,IT本来就是要不断学习,不断积累才会在行业中能走的更远

没有更多推荐了,返回首页