2.2 OPenCV直接访问像素和filter2D函数两种方式实现图片对比度增强

矩阵的Mask操作是非常简单的.就是我们根据掩码矩阵(Kernel矩阵)重新计算图像总每个像素的值.此Mask值用于调整相邻像素对新像素的影响程度.从数学的角度来看,是用我们指定的值做了一个加权平均数.

测试用例

要实现对图像对比度的增强,基本上我们会对图像中的每一个像素应用下面的公式:
在这里插入图片描述
上图中第一种表示方式是使用公式,第二种表示法是使用Mask,其是第一种表示法的压缩版本.你可以通过将Mask矩阵的中心((由0 - 0索引标记的大写字母))放在你想计算的像素上,然后将像素值与重叠部分矩阵值相乘.以上两种表示方法是一样的,但是在大型矩阵的情况下,使用后一种表示法更容易理解.

实例源码

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
static void help(char* progName)
{
    cout << endl
        <<  "This program shows how to filter images with mask: the write it yourself and the"
        << "filter2d way. " << endl
        <<  "Usage:"                                                                        << endl
        << progName << " [image_path -- default lena.jpg] [G -- grayscale] "        << endl << endl;
}
void Sharpen(const Mat& myImage,Mat& Result);
int main( int argc, char* argv[])
{
    help(argv[0]);
    const char* filename = argc >=2 ? argv[1] : "../sky.jpg";
    Mat src, dst0, dst1;
    if (argc >= 3 && !strcmp("G", argv[2]))
        src = imread( samples::findFile( filename ), IMREAD_GRAYSCALE);
    else
        src = imread( samples::findFile( filename ), IMREAD_COLOR);
    if (src.empty())
    {
        cerr << "Can't open image ["  << filename << "]" << endl;
        return EXIT_FAILURE;
    }
    namedWindow("Input", WINDOW_AUTOSIZE);/**创建自适应图片大小的显示窗口 Input*/
    namedWindow("Pixel", WINDOW_AUTOSIZE);/**创建自适应图片大小的显示窗口 Pixel*/
    namedWindow("Filter", WINDOW_AUTOSIZE);/**创建自适应图片大小的显示窗口 Filter*/
    imshow( "Input", src );/**Input窗口显示原始图像*/

    /** 使用像素访问基本方法实现对比度增强 */
    double t = (double)getTickCount();
    Sharpen( src, dst0 );/**输出图像存储在dst0*/
    t = ((double)getTickCount() - t)/getTickFrequency();
    cout << "Hand written function time passed in seconds: " << t << endl;
    imshow( "Pixel", dst0 );
    // waitKey();

    /** 使用filter2D()函数实现对比度增强 */
    Mat kernel = (Mat_<char>(3,3) <<  0, -1,  0,
                                   -1,  5, -1,
                                    0, -1,  0);
    t = (double)getTickCount();
    filter2D( src, dst1, src.depth(), kernel );/**输出图像存储在dst1*/
    t = ((double)getTickCount() - t)/getTickFrequency();
    cout << "Built-in filter2D time passed in seconds:     " << t << endl;
    imshow( "Filter", dst1 );
    waitKey();
    return EXIT_SUCCESS;
}
/**
 * @brief  利用公式来实现对比度增强
 * @param  myImage          源图片
 * @param  Result           对比度增强后的输出图片
 */
void Sharpen(const Mat& myImage,Mat& Result)
{
    CV_Assert(myImage.depth() == CV_8U);  // accept only uchar images
    const int nChannels = myImage.channels();
    Result.create(myImage.size(),myImage.type());
    for(int i = 1 ; i < myImage.rows-1; ++i)
    {
        const uchar* previous = myImage.ptr<uchar>(i - 1);/**上一行*/
        const uchar* current  = myImage.ptr<uchar>(i    );/**当前行*/
        const uchar* next     = myImage.ptr<uchar>(i + 1);/**下一行*/
        uchar* output = Result.ptr<uchar>(i);
        for(int j= nChannels;j < nChannels*(myImage.cols-1); ++j)
        {
            *output++ = saturate_cast<uchar>(5*current[j]
                         - previous[j] - next[j]-current[j-nChannels]-current[j+nChannels]);
        }
    }
    /**边界直接设置为0*/
    Result.row(0).setTo(Scalar(0));
    Result.row(Result.rows-1).setTo(Scalar(0));
    Result.col(0).setTo(Scalar(0));
    Result.col(Result.cols-1).setTo(Scalar(0));
}

直接访问像素利用公式计算进行对比度增强

下面让我们来看看如何使用像素访问基本方法或者filter2D函数来实现这一功能:

void Sharpen(const Mat& myImage,Mat& Result)
{
    CV_Assert(myImage.depth() == CV_8U);  // accept only uchar images
    const int nChannels = myImage.channels();
    Result.create(myImage.size(),myImage.type());
    for(int j = 1 ; j < myImage.rows-1; ++j)
    {
        const uchar* previous = myImage.ptr<uchar>(j - 1);
        const uchar* current  = myImage.ptr<uchar>(j    );
        const uchar* next     = myImage.ptr<uchar>(j + 1);
        uchar* output = Result.ptr<uchar>(j);
        for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i)
        {
            *output++ = saturate_cast<uchar>(5*current[i]
                         -current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);
        }
    }
    Result.row(0).setTo(Scalar(0));
    Result.row(Result.rows-1).setTo(Scalar(0));
    Result.col(0).setTo(Scalar(0));
    Result.col(Result.cols-1).setTo(Scalar(0));
}

首先我们确保输入的图像是unsigned char格式的.为此我们使用cv::CV_Assert()函数,当内部表达式为false的时候会主动抛出异常.

 CV_Assert(myImage.depth() == CV_8U);  // 表明只接收unsigned char 格式数据

然后我们创建一个与输入图像使用相同数据类型和大小的输出图像.正如你在图像存储章节看到的,根据通道的数量,我们可能有一个子列或者多个子列.我们将通过指针进行遍历,因此元素的个数取决于数据类型.

    const int nChannels = myImage.channels();
    Result.create(myImage.size(),myImage.type());

我们使用C语言中普通的[]操作符来访问像素.因为我们需要同时访问很多行数据,因此我们需要获取每一行数据的指针(上一行,当前行,下一行).另外我们还需要另外一个指针来指向我们存储计算结果的地址.然后也对其使用[]进行访问.我们要向前移动输出指针,只需每次操作后对此指针进行自增即可(每次增加一个字节)

  for(int j = 1 ; j < myImage.rows-1; ++j)
    {
        const uchar* previous = myImage.ptr<uchar>(j - 1);
        const uchar* current  = myImage.ptr<uchar>(j    );
        const uchar* next     = myImage.ptr<uchar>(j + 1);
        uchar* output = Result.ptr<uchar>(j);
        for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i)
        {
            *output++ = saturate_cast<uchar>(5*current[i]
                         -current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);
        }
    }

在图像边界上,上面的表达式会出现不存在的像素点(-1,-1).在这些点公式是没有定义的,一个较为简单的解决办法是在这些点上不使用Kernel矩阵,例如直接设置边界像素为0.

    Result.row(0).setTo(Scalar(0));
    Result.row(Result.rows-1).setTo(Scalar(0));
    Result.col(0).setTo(Scalar(0));
    Result.col(Result.cols-1).setTo(Scalar(0));

fileter2D进行对比度增强

在图像处理中使用过滤器是非常常见的,因此OpenCV中有一个专门的函数负责应用mask(掩码),某些地方可能称之为kernel.因此,首先需要定义一个保存掩码的对象:

Mat kernel = (Mat_<char>(3,3) <<  0, -1,  0,
                                   -1,  5, -1,
                                    0, -1,  0);

然后调用filter2D()函数,并指定输入,输出图像,以及所使用的mask矩阵.

filter2D( src, dst1, src.depth(), kernel );

filter2D函数甚至有第5个可选参数来指定mask矩阵的中心,第6个可选参数用于将过滤后的像素存储到K之前添加一个可选值,第7个可选参数用于确定在操作未定义的区域(边界)中会执行何种操作.这个函数更简短,高效.通常比手动编码的方法更快.

相关函数说明

 /**
  * @brief 创建一个显示窗口,winName
  * @param winname 新建的窗口名称
  * @param flags 显示窗口类型 
  * WINDOW_AUTOSIZE:窗口大小自适应图片大小,并且不可手动更改
  * WINDOW_NORMAL:用户可以改变窗口大小
  * WINDOW_OPENGL:窗口创建的时候会使用OpenGL
 */
 void nameWindow(const string& winname,int flags = WINDOW_AUTOSIZE) ;

运行结果

gaoy@gy-PC:~/GY/git/open-cv-learning/Demo/Core/2_mask/build$ ./mask 

This program shows how to filter images with mask: the write it yourself and thefilter2d way. 
Usage:
./mask [image_path -- default lena.jpg] [G -- grayscale] 


(Input:11109): GLib-GObject-CRITICAL **: 18:36:38.050: g_value_set_boxed: assertion 'G_VALUE_HOLDS_BOXED (value)' failed
Hand written function time passed in seconds: 0.0448108
Built-in filter2D time passed in seconds:     0.00679664

运行效果图

在这里插入图片描述
源码下载

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值