【图像处理100问】图像处理之各种像素操作效果(上)

2020-07-14 10:28:51 浏览数 (1)

学校把很多考试都放在暑假考了,我们专业有6科,分布在一个月内。又要备赛数学建模,快乐暑假已经被榨干了... ...

所以只能利用碎片时间更一篇上次给大家介绍的《视觉图像处理100问》了,因为有原作者写好的代码,所以比较节省时间。关于这个具体资料看上篇文章:

【资源分享1】日本同行整理的视觉处理100问

最近在筹划一篇详解分水岭算法的文章,大家等等吧~

问题一:通道交换

这道题如果用opencv的cvtColor函数写很简单,cvtColor函数可以在绝大部分格式之间转换,具体见下图(截自毛星云《opencv3编程入门》):

我们基于像素操作,自定义一个函数channel_swap()来实现BGR->RGB转换的功能:

代码语言:javascript复制
// 【1】通道转换
cv::Mat channel_swap(cv::Mat img) {
  // get height and width
  int width = img.cols;
  int height = img.rows;

  // prepare output
  cv::Mat out = cv::Mat::zeros(height, width, CV_8UC3);

  // each y, x
  for (int y = 0; y < height; y  ) {
    for (int x = 0; x < width; x  ) {
      // R -> B
      out.at<cv::Vec3b>(y, x)[0] = img.at<cv::Vec3b>(y, x)[2];
      // B -> R
      out.at<cv::Vec3b>(y, x)[2] = img.at<cv::Vec3b>(y, x)[0];
      // G -> G
      out.at<cv::Vec3b>(y, x)[1] = img.at<cv::Vec3b>(y, x)[1];
    }
  }
  return out;
}

程序很简单,就是逐像素主通道进行变换就可以了。

对比opencv的API:cvtColor和自定义函数的运行效果:

问题二:图像转灰度图

RGB转灰度图就是根据上图公式,同样可以根据像素操作来实现:

代码语言:javascript复制
//【2】BGR -> Gray
cv::Mat BGR2GRAY(cv::Mat img) {
  // get height and width
  int width = img.cols;
  int height = img.rows;

  // prepare output
  cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1);

  // each y, x
  for (int y = 0; y < height; y  ) {
    for (int x = 0; x < width; x  ) {
      // BGR -> Gray
      out.at<uchar>(y, x) = 0.2126 * (float)img.at<cv::Vec3b>(y, x)[2] 
          0.7152 * (float)img.at<cv::Vec3b>(y, x)[1] 
          0.0722 * (float)img.at<cv::Vec3b>(y, x)[0];
    }
  }
  return out;
}

问题三:图像二值化(THresholding)

二值化原理很简单了,利用像素操作加if判断就可以实现:

代码语言:javascript复制
// Gray -> Binary
cv::Mat Binarize(cv::Mat gray, int th){
  int width = gray.cols;
  int height = gray.rows;

  // prepare output
  cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1);

  // each y, x
  for (int y = 0; y < height; y  ){
    for (int x = 0; x < width; x  ){
      // Binarize
      if (gray.at<uchar>(y, x) > th){
        out.at<uchar>(y, x) = 255;
      } else {
        out.at<uchar>(y, x) = 0;
      }
    }
  }
  return out;
}

下面对比一下opencv的Threshold()函数和我们自定义函数Binarize()函数:

代码语言:javascript复制
int main()
{
  Mat srcImage, grayImage,dstImage;

  srcImage = imread("御坂美琴/1.png");//读取图像
  imshow("原图BGR", srcImage);
  cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);//转灰度
  threshold(grayImage, dstImage, 125, 255, THRESH_BINARY);

  imshow("threshold函数阈值化", dstImage);

  Mat out = Binarize(grayImage,125);
  imshow("自定义函数阈值化", out);//自定义函数

  waitKey();
  return 0;
}

问题四:大津二值化算法(Otsu's Method)

代码语言:javascript复制
// Gray -> Binary 输入图像为灰度图
cv::Mat Binarize_Otsu(cv::Mat gray){
  int width = gray.cols;
  int height = gray.rows;

  // determine threshold
  double w0 = 0, w1 = 0;  //被阈值 t分开的两个类中的像素数占总像素数的比率(满足相加等于1 )
  double m0 = 0, m1 = 0; //是这两个类的像素值的平均值
  double max_sb = 0, sb = 0;
  int th = 0;
  int val;

  // 遍历阈值t的可能值寻求最大值
  for (int t = 0; t < 255; t  ){
    w0 = 0;
    w1 = 0;
    m0 = 0;
    m1 = 0;
    for (int y = 0; y < height; y  ){  //遍历图像像素点
      for (int x = 0; x < width; x  ){
        val = (int)(gray.at<uchar>(y, x)); //获取该点的灰度值

        if (val < t){
          w0  ;
          m0  = val;
        } else {
          w1  ;
          m1  = val;
        }
      }
    }

    m0 /= w0;//计算大于,小于阈值t的两类像素的像素值的平均值m0 ,m1
    m1 /= w1;
    w0 /= (height * width);//计算大于,小于阈值t的两类像素的个数占总个数的比例w0,w1
    w1 /= (height * width);
    sb = w0 * w1 * pow((m0 - m1), 2); //计算sb的平方

    if(sb > max_sb){ //更新最大值
      max_sb = sb;
      th = t;
    }
  }

  std::cout << "threshold:" << th << std::endl;//打印阈值

  //获取阈值后,对图像进行阈值化
  // prepare output
  cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1);

  // each y, x
  for (int y = 0; y < height; y  ){
    for (int x = 0; x < width; x  ){
      // Binarize
      if (gray.at<uchar>(y, x) > th){
        out.at<uchar>(y, x) = 255;
      } else {
        out.at<uchar>(y, x) = 0;
      }

    }
  }

  return out;
}

处理效果:

0 人点赞