文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

目标

在这个教程中你将学到:

  • 获取像素值
  • 初始化一个0矩阵
  • 学习 saturate_cast 的功能,以及体会它的重要性
  • 感受一下像素级别的变换

理论

笔记

下面名词定义来源于 Richard Szeliski 编写的书籍 Computer Vision: Algorithms and Applications 

图像处理

  • 一般的图像处理算子,指的是一个函数,这个函数接受一个或多个图像输入,输出一个图像。
  • 图像变换可以看做是:
    • 点算子(像素级别的变换)
    • 邻域算子(区域范围的变换)

像素变换

第 1 段(可获 2 积分)
  • 在这种类型的图像处理中,每个输出像素的值只依赖于对应的输入像素。(可能会依赖一些全局设定或者参数)
  • 例如:颜色矫正算子和变换算子,还有亮度和对比度调节算子等。
亮度和对比度调节
  • 两个常用的点处理是:与常数相乘和相加:

    g(x) = \alpha f(x) + \beta

  • 参数 \alpha> 0 被称为增益,\beta 被称为 偏移 ; 有些时候,这两个参数可以达到分别控制对比度和亮度的效果。

  • 如果认为 f(x) 是源图像, g(x) 则是输出图像. 我们将表达式写成如下形式:

    g(i,j) = \alpha \cdot f(i,j) + \beta

    i ,j 是像素点的坐标。.

第 2 段(可获 2 积分)

Code

  • 下面的代码展示了操作 g(i,j) = \alpha \cdot f(i,j) + \beta :
#include <cv.h>
#include <highgui.h>
#include <iostream>

using namespace cv;

double alpha; /**< Simple contrast control */
int beta;  /**< Simple brightness control */

int main( int argc, char** argv )
{
 /// Read image given by user
 Mat image = imread( argv[1] );
 Mat new_image = Mat::zeros( image.size(), image.type() );

 /// Initialize values
 std::cout<<" Basic Linear Transforms "<<std::endl;
 std::cout<<"-------------------------"<<std::endl;
 std::cout<<"* Enter the alpha value [1.0-3.0]: ";std::cin>>alpha;
 std::cout<<"* Enter the beta value [0-100]: "; std::cin>>beta;

 /// Do the operation new_image(i,j) = alpha*image(i,j) + beta
 for( int y = 0; y < image.rows; y++ )
    { for( int x = 0; x < image.cols; x++ )
         { for( int c = 0; c < 3; c++ )
              {
      new_image.at<Vec3b>(y,x)[c] =
         saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
             }
    }
    }

 /// Create Windows
 namedWindow("Original Image", 1);
 namedWindow("New Image", 1);

 /// Show stuff
 imshow("Original Image", image);
 imshow("New Image", new_image);

 /// Wait until user press some key
 waitKey();
 return 0;
}
第 3 段(可获 2 积分)

解释说明

  1. 我们首先来定义存贮 \alpha 和\beta的参数 ,供用户使用:

    double alpha;
    int beta;
    
  2. 使用imread方法载入图像并用Mat对象接收:

    Mat image = imread( argv[1] );
    
  3. 现在由于将对图像做一些转换, 我们需要一个新的Mat对象来存贮转换结果. 并且, 我们希望这个新对有如下特征:

    • 初始化像素值为0
    • 与原图像具有相同的大小和类型
    Mat new_image = Mat::zeros( image.size(), image.type() );
    

    我们看到Mat::zeros 返回了一个 image.size() 和 image.type()的Mat类型并且值被初始化为0

  4. 现在,为了执行操作 g(i,j) = \alpha \cdot f(i,j) + \beta 我们将获取图像中的 每个像素. 由于我们只操作BGR三通道图像, 每个像都有三个值 (B, G and R), 所以我们会分别获取它们. 这是一段这样做的代码:

    for( int y = 0; y < image.rows; y++ )
       { for( int x = 0; x < image.cols; x++ )
            { for( int c = 0; c < 3; c++ )
                 { new_image.at<Vec3b>(y,x)[c] =
                             saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta ); }
       }
       }
    

    注意以下内容:

    • 为了获取图像中的每个值我们使用这样的语法: image.at<Vec3b>(y,x)[c] y表示行,x表示列,c是来获取通道 R, G or B (0, 1 or 2).
    • Since the operation \alpha \cdot p(i,j) + \beta can give values out of range or not integers (if \alpha is float), we use saturate_cast to make sure the values are valid.
  5. 最后我们创建一个窗口来显示图像,采用最常规的方法.

    namedWindow("Original Image", 1);
    namedWindow("New Image", 1);
    
    imshow("Original Image", image);
    imshow("New Image", new_image);
    
    waitKey(0);
    
第 4 段(可获 2 积分)

注意:

当要获取每个像素值时,除了使用for循环,还可以简单的使用下面的命令:

image.convertTo(new_image, -1, alpha, beta);

convertTo 会高效的执行new_image = a*image + beta.。 前面之所以用for循环,只不过是演示一下如何获取每个像素点的值.。任何情况下,两种方法返回的是相同的结果,但是使用经过优化的convertTo函数,速度更快 。

Result

  • 运行代码,其中 \alpha = 2.2 and \beta = 50

    $ ./BasicLinearTransforms lena.jpg
    Basic Linear Transforms
    -------------------------
    * Enter the alpha value [1.0-3.0]: 2.2
    * Enter the beta value [0-100]: 50
    
  • 我们得到:

第 5 段(可获 2 积分)

文章评论