2017-11-24 22:42:45 xxxqcbQ 阅读数 244

        一直想记录自己图像处理算法修炼心路历程,今天终于鼓足勇气写下第一篇,万事开头难。

        算法写了快4个月了,目前写的比较多的是基础算法,比如通用的圆、直线检测算法,还真不能小瞧这类算法,想在工程上做到又快又准,真的不是一件容易的事情。这里应该算是有bug的,又快又好应当有个明确的量化指标。

        对于圆检测的基本逻辑是:Sobel/Canny算子处理→拿出边界→边界筛选→最小二乘拟合(对噪声很敏感)。


2015-12-03 09:15:04 liu_xiao_cheng 阅读数 1874

主要是一些算法基础函数,千里之行始于足下,记下这些基础函数迟早会有用武之地。

我们在做一些算法分析时候可以根据不同的目的转换不同的颜色空间,例如:分析目标颜色时候可以转换成RGB颜色空间,

分析亮度饱和度就要转换成HSV颜色空间等等。


#define RGB565_R(p) ((((p) & 0xF800) >> 11) << 3)
#define RGB565_G(p) ((((p) & 0x7E0 ) >> 5)  << 2)
#define RGB565_B(p) ( ((p) & 0x1F  )        << 3)
#define MAKE_RGB565(r,g,b) ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3))

#define RGBA_A(p) (((p) & 0xFF000000) >> 24)
#define RGBA_R(p) (((p) & 0x00FF0000) >> 16)
#define RGBA_G(p) (((p) & 0x0000FF00) >>  8)
#define RGBA_B(p)  ((p) & 0x000000FF)
#define MAKE_RGBA(r,g,b,a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))


inline byte RGB2GRAY(CvScalar &rgb)
{
    float max = MAX(rgb.val[0],MAX(rgb.val[1],rgb.val[2]));
    float min = MIN(rgb.val[0],MIN(rgb.val[1],rgb.val[2]));
    float mid = MIN(rgb.val[0],MAX(rgb.val[1],rgb.val[2]));

    float ret = max * 0.3 + mid * 0.59 + min * 0.11;

    return (byte)ret;
}

inline CvScalar RGB2HSV(CvScalar &rgb)
{
 double maxc = MAX(rgb.val[2],MAX(rgb.val[1],rgb.val[0]));
 double minc = MIN(rgb.val[2],MIN(rgb.val[1],rgb.val[0]));
 
 CvScalar hsv = CV_RGB(0,0,0);

 if (maxc > 0)
 {
  hsv.val[2] = maxc;
  hsv.val[1] = (maxc - minc)/maxc;
  double delta = maxc - minc;
  if ((maxc - rgb.val[2]) < FLOATPRECISION)
  {
   hsv.val[0] = (rgb.val[1] - rgb.val[0])*60/delta;
  }else if ((maxc - rgb.val[1]) < FLOATPRECISION)
  {
   hsv.val[0] = (rgb.val[0] - rgb.val[2])*60/delta + 180;
  }else
  {
   hsv.val[0] = (rgb.val[2] - rgb.val[1])*60/delta + 240;
  }

  if (hsv.val[0] < 0)
  {
   hsv.val[0] += 360;
  }
 }

 return hsv;
};


inline CvScalar RGB2HSVP20(CvScalar &rgb)
{
    double maxc = MAX(rgb.val[2],MAX(rgb.val[1],rgb.val[0]));
    double minc = MIN(rgb.val[2],MIN(rgb.val[1],rgb.val[0]));

    CvScalar hsv = CV_RGB(0,0,0);

    if (maxc > 0)
    {
        hsv.val[2] = maxc;
        hsv.val[1] = (maxc - minc)/maxc;
        double delta = maxc - minc;
        if ((maxc - rgb.val[2]) < FLOATPRECISION)
        {
            hsv.val[0] = (rgb.val[1] - rgb.val[0])*60/delta;
        }else if ((maxc - rgb.val[1]) < FLOATPRECISION)
        {
            hsv.val[0] = (rgb.val[0] - rgb.val[2])*60/delta + 180;
        }else
        {
            hsv.val[0] = (rgb.val[2] - rgb.val[1])*60/delta + 240;
        }

        if (hsv.val[0] < 0)
        {
            hsv.val[0] += 360;
        }

        if (hsv.val[0] > 320)
        {
            hsv.val[0] -= 320;
        }else
        {
            hsv.val[0] += 40;
        }
    }

    return hsv;
};


inline CvScalar RGB2Lab(CvScalar &rgb)
{
 double X = 0.433910 * rgb.val[2]/255 + 0.376220 * rgb.val[1]/255 + 0.189860 * rgb.val[0]/255;

 double Y = 0.212649 * rgb.val[2]/255 + 0.715169 * rgb.val[1]/255 + 0.072182 * rgb.val[0]/255;

 double Z = 0.017756 * rgb.val[2]/255 + 0.109478 * rgb.val[1]/255 + 0.872915 * rgb.val[0]/255;

 CvScalar Lab;

 if (Y > 0.008856)
 {
  Lab.val[0] = 116*Y/3;
 }
 else
 { 
  Lab.val[0] = 903.3*Y;
 }

 double fx = 0;

 if (X > 0.008856)
 {
  fx = X/3;
 }
 else
 {
  fx = 7.787*X+16/116;
 }
 
 double fy = 0;

 if (Y > 0.008856)
 {
  fy = Y/3;
 }
 else
 {
  fy = 7.787*Y+16/116;
 }

 double fz = 0;

 if (Z > 0.008856)
 {
  fz = Z/3;
 }
 else
 {
  fz = 7.787*Z+16/116;
 }
 
 Lab.val[1] = 500*(fx - fy);
 Lab.val[2] = 200*(fy - fz);
 
 return Lab;
};


inline CvScalar RGB2YCrCb(CvScalar &rgb)
{

 CvScalar YCrCb = cvScalar(0,0,0);

 YCrCb.val[0] = 0.299*rgb.val[2] + 0.587*rgb.val[1] + 0.114*rgb.val[0];

 YCrCb.val[1]=(rgb.val[2]-YCrCb.val[0])*0.713;
 YCrCb.val[2]=(rgb.val[0]-YCrCb.val[0])*0.564;


 return YCrCb;
};


2015-12-26 14:02:25 liu_xiao_cheng 阅读数 3777

1.原理:

霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,它通过一种投票算法检测具有特定形状的物体。该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换结果。霍夫变换于1962年由Paul Hough 首次提出[53],后于1972年由Richard Duda和Peter Hart推广使用[54],经典霍夫变换用来检测图像中的直线,后来霍夫变换扩展到任意形状物体的识别,多为圆和椭圆。
霍夫变换运用两个坐标空间之间的变换将在一个空间中具有相同形状的曲线或直线映射到另一个坐标空间的一个点上形成峰值,从而把检测任意形状的问题转化为统计峰值问题,上一节中已经介绍了车道的直线特征,本节中介绍hough变换检测直线的原理和检测结果。
我们知道,一条直线在直角坐标系下可以用y=kx+b表示, 霍夫变换的主要思想是将该方程的参数和变量交换,即用x,y作为已知量k,b作为变量坐标,所以直角坐标系下的直线y=kx+b在参数空间表示为点(k,b),而一个点(x1,y1)在直角坐标系下表示为一条直线y1=x1·k+b,其中(k,b)是该直线上的任意点。为了计算方便,我们将参数空间的坐标表示为极坐标下的γ和θ。因为同一条直线上的点对应的(γ,θ)是相同的,因此可以先将图片进行边缘检测,然后对图像上每一个非零像素点,在参数坐标下变换为一条直线,那么在直角坐标下属于同一条直线的点便在参数空间形成多条直线并内交于一点。因此可用该原理进行直线检测。


参数空间变换结果

如图 上图所示,对于原图内任一点(x,y)都可以在参数空间形成一条直线,以图中一条直线为例有参数(γ,θ)=(69.641,30°),所有属于同一条直线上的点会在参数空间交于一点,该点即为对应直线的参数。由该图中所有直线所得到的(γ,θ)在参数空间中得到一系列对应曲线见图 4‑14 霍夫统计变换结果。由霍夫变换检测结果见图 下所示。

2.霍夫变换在程序中实现的步骤如下:


3.基于OpenCV的代码实现

int  picProcessBasics::IMGHough(IplImage* pImg)
 {
  if(NULL == pImg)
   return -1;

  CvMemStorage *storage = cvCreateMemStorage();
  CvSeq *lines = 0;

 IplImage* tempImage= cvCreateImage(cvGetSize(pImg), 8, 1);

  if(pImg->nChannels != 1)
  {
   cvCvtColor(pImg,tempImage,CV_RGB2GRAY);
  }
  else
   cvCopy(pImg,tempImage,0);
 
  /*** 1.计算图像梯度并对其设置门限得到二值图像 ***/
  cvCanny(tempImage,tempImage,40,90);
  /*2.调用OpenCV霍夫变换函数求取得到直线点集*/
 lines = cvHoughLines2(tempImage,storage,CV_HOUGH_STANDARD,1,CV_PI/180 ,150 ,0 ,0);
 for(int i = 0;i<MIN(lines->total,100);i++){  
  float *line = (float*)cvGetSeqElem(lines,i);
  float rho = line[0];
  float threta = line[1];
  CvPoint pt1, pt2;
  double a = cos(threta),b = sin(threta);
  double x0 = a*rho, y0 = b * rho; 
  int len=500;//求出直线长度的1/2
  pt1.x = cvRound(x0 + len*(-b)); 
  pt1.y = cvRound(y0 + len*(a));  
  pt2.x = cvRound(x0 - len*(-b));
  pt2.y = cvRound(y0 - len*(a)); 
  cvLine(pImg,pt1,pt2,CV_RGB(255,0,0),1,8); 
 }
  cvReleaseImage(&tempImage);
  cvReleaseMemStorage(&storage);

 return 0;
 }

处理前图像:

处理后检测并画出直线图像:


2015-12-25 16:15:44 liu_xiao_cheng 阅读数 6213
    形态学一般是使用二值图像,进行边界提取,骨架提取,孔洞填充,角点提取,图像重建。

    基本的算法:膨胀腐蚀,开操作,闭操作,击中击不中变换

     边界提取主要涉及腐蚀和求补集操作

代码如下:

int  picProcessBasics::IMGEdgeExtracting(IplImage* pImg,IplImage* pDestImg,double threshold,int pos)
{
 if(NULL == pImg || NULL == pDestImg)
  return -1;
 if(pDestImg->nChannels!=1 )
 {
  cout<<"It's not gray image!"<<endl;
  return -1;
 }

 IplImage* tempImage= cvCreateImage(cvGetSize(pImg), 8, 1);
 if(pImg->nChannels != 1)
 {
  cvCvtColor(pImg,tempImage,CV_RGB2GRAY);
 }
 else
  cvCopy(pImg,tempImage,0);
 
 //转换成二值图像
 cvThreshold(tempImage, pDestImg, threshold, 255, CV_THRESH_BINARY);

 
 IplConvKernel *element = cvCreateStructuringElementEx( pos*2+1, pos*2+1, pos, pos, CV_SHAPE_RECT, 0 );
 cvErode( tempImage, tempImage, element, 1); // 侵蚀,磨蚀
 cvReleaseStructuringElement( &element );

 for(int i = 0; i < tempImage->height; i++){ 
  for(int j = 0; j < tempImage->width; j++){ 
 
   pDestImg->imageData[pDestImg->widthStep * i + j ]=pDestImg->imageData[pDestImg->widthStep * i + j ] - tempImage->imageData[tempImage->widthStep * i + j ];
        } 
    }
 cvReleaseImage(&tempImage);

 return 0;
}

处理前图片:


提取边界的图片:


2015-12-27 17:12:24 liu_xiao_cheng 阅读数 1334

关于阈值分割前面已经写过一篇但是最重要的部分阈值的求取没有说明,本文主要讲怎么求取最佳分割门限,此处讲的是全局门限,复杂的图像只需把图像分成若干小块然后对每一块求取最佳分割门限然后分割即可。

步骤讲解:

代码实现如下:

int  picProcessBasics::IMGthresholdSeg(IplImage* pImg,int T0)
 {
  if(NULL == pImg)
   return -1;

  if(pImg->nChannels != 1)
  {
 cout<<"param error"<<endl;
 return -1;
  }

  uchar* old_data = (uchar*)pImg->imageData; 
 
   int numG1 = 0;//记录大于T值得像素个数
   int numG2 = 0;
   long int sumG1 = 0;//记录大于T值的像素灰度级之和
    long int sumG2 = 0;
 int valueG1 = 0;//记录大于T值得像素平均灰度值
   int valueG2 = 0;
   long int sum = 0; 
   int resT=0;

 //1.求图像灰度级平均值
 for(int i = 0;i < pImg->height;i++){ 
  for(int j = 0; j < pImg->width; j++){ 
 
   sum += old_data[pImg->widthStep * i + j ];   
        } 
    }

 int tempT=sum/(pImg->height*pImg->width);//将整幅图像灰度级的平均值作为门限T的初始值
 resT=tempT;
 do
 {
  tempT=resT;
  for(int i = 0;i < pImg->height;i++){ 
   for(int j = 0; j < pImg->width; j++){ 
 
    if(old_data[pImg->widthStep * i + j ] > tempT) 
    {
     sumG1 += old_data[pImg->widthStep * i + j ];
     numG1++;
    }
    else
    {
     sumG2 += old_data[pImg->widthStep * i + j ];
     numG2++;
    }
   } 
  }
  valueG1=sumG1/numG1;
  valueG2=sumG2/numG2;
  resT=(valueG1+valueG2)/2;

 }while(abs(resT-tempT) > T0);

 for(int i = 0;i < pImg->height;i++){ 
  for(int j = 0; j < pImg->width; j++){ 
 
   if(old_data[pImg->widthStep * i + j ] > resT) 
   {
    old_data[pImg->widthStep * i + j ]=255;
   }
   else
   {
    old_data[pImg->widthStep * i + j ]=0;
   }
  } 
 }

 return 0;
 }

原图像:

分割后图像:



线性变换和伽马变换C++实现

博文 来自: liu_xiao_cheng

中值滤波 C++实现及opencv函数

博文 来自: liu_xiao_cheng
没有更多推荐了,返回首页