opencv初识(一)

环境配置:Visual Studio 2015 + openCV3.1

opencv常用操作介绍

调用摄像头

#include <opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;

int main() {
    Mat frame;   // Mat 即 Matrix
    VideoCapture cap(0);// 摄像头,视频文件,0(default),1,2 摄像头,  文件路径都视频

    while (true)
    {
        cap >> frame;   // 读入流
    // imshow函数用于显示图片或视频,并且该函数相当于一个线程
        imshow("frame", frame); // 参数1--句柄
        waitKey(10);  // 参数单位为毫秒,为0时任意延迟, 必须加
    }

    system("pause");
  return 0;
}

读取图片

  #include <opencv.hpp>
  #include <iostream>
  using namespace cv;
  using namespace std;

  int main() {
    Mat imggray = imread("image.jpg", 1); // 参数1为图片路径,参数2:为0时是灰度图,为1时是彩色图
    imshow("123", imggray);
    waitKey(0);
    return 0;
  }

关于灰度图和彩色图:

  • 灰度图是单通道存储,值为0~255
  • 彩色图是三通道存储,即 R(red) G(green) B(blue)
    • 需要注意的是在图像矩阵中,列数为行数的三倍,并且是以 B G R 的顺序存储的

_ 那么如何把彩色图变成灰度图呢?_

只需要一行代码就可以了

在上面代码的基础上,在imshow函数之前加上如下一行代码


cvtColor(imggray, imggray, CV_RGB2GRAY);

cvtColor函数,其实就是 convert color,用来转换颜色的,有四个参数,前面两个参数是Mat类型,第三个参数是宏常量,这里看得出来我们传入的宏常量的作用是想让图片变成灰度图,第四个参数是一个默认值,默认为0,这里可以不用管。

我们知道图像其实都是由像素组成的,那么如果我们想获取图像的上的像素值怎么做呢?这里我们以读取图像矩阵中(1,1)上的像素为例

  cout << (int) imggray.at<uchar>(1, 1) << endl; // 读取第一行第一列的像素值 ,并转换成int输出

openCV也有一些常用操作,这里总结一下:

创建一个 5 * 5 的浮点型矩阵

  Mat image = Mat(5, 5 ,CV_64FC1);

Ps
CV_64FC1即单通道double型
F即float浮点型,是32位的,那么64位的double表示方法即64F_(注意在opencv中没有D的表示)_,类似的还有8位无符号字符 uchar – 8U
C 即 Channel,即通道

创建一个 5 * 5 的元素全为 0 矩阵

  Mat image = Mat::zeros(5, 5, CV_64FC1);

创建一个 5 * 5 的 元素全为 1 的矩阵

  Mat image = Mat::ones(5, 5, CV_64FC1);

创建一个 5 * 5 的单位矩阵

  Mat image = Mat::eye(5, 5, CV_64FC1);

还有一些对于矩阵的操作

  image.inv();   // 矩阵转置
  image.copyTo();   // 矩阵拷贝

什么是卷积?

卷积是两个变量在某范围内相乘后求和的结果

摘自百度百科

听着挺玄乎,那么我现在以一个图来简单解释一下

在上面这个图中,我们可以看到有一个堆满数字的矩阵,这是一个二维的像素矩阵,那图中左上角的 3 * 3 的矩阵来说
__

/       \
| 0 0 0 |
| 0 1 1 |
| 0 1 2 |
\       /

矩阵中心的值(就是最中间的那个1),我们现在叫它 源像素(Source pixel)

现在又有一个卷积核(Convolution kernel),或者叫 滤波器矩阵

/       \
| 4 0 0 |
| 0 0 0 |
| 0 0 -4|
\       /

接下来我将像素矩阵和卷积核(滤波器矩阵)这两个矩阵进行逐个元素相乘求和,操作如下:

4*0 + 0*0 + 0*0 + 0*0 + 1*0 + 1*0 + 1*0 + 0*0 + 1*0 + 2*(-4) = -8

这时我们得到了一个值:-8,并且放在矩阵的中间,我们称这个值为新像素值(New pixel value)或者目标像素(Destination pixel)

那么上面的对两个矩阵逐个元素相乘求和的操作,就叫做 卷积

那么卷积能干什么呢?

我们来举个例子

下面有一幅图片

然后我想将图片变成一种浮雕的效果,如下

我们就可以利用卷积来实现该效果,但是我们先不用openCV的api操作,我们先用自己的算法实现

思路如下:

  • 首先,就是对图像的矩阵进行遍历,因为是二维的,所以我们需要一个二重循环
  • 其次,我们要让图像矩阵与卷积核逐个元素相乘求和,那么我们就需要又需要一个二重循环来控制

也就是说我们需要四重循环

注意
+ 整个图像的边缘是无法操作的,因为我们是对矩阵中每一个元素周围3*3的范围内进行操作,所以对图像矩阵循环遍历时,应该从1开始,从 行或列 - 1结束
+ 能否实现浮雕效果的关键在于这个卷积核,因为这个浮雕具有一种3d的效果,所以我们需要保证像素值一边为负,一边为正,并且形成一个对角,就会有一种侧面关照浮雕的立体效果,这里我们可以直接用上面图片中的那个3 * 3的卷积核

Mat image = imread("flower.jpg", 1);        // 读取图片
cvtColor(image, image, CV_RGB2GRAY);    // 将图片转换为灰度图

Mat dimage = Mat(image.rows, image.cols, CV_8UC1);  // 建立一个跟image图片行,列相等,列少2的矩阵,类型为uchar
Mat model = Mat(3, 3, CV_64FC1);    // 创立一个3 * 3 的矩阵,类型为double,卷积核 (滤波矩阵)

// 给model矩阵,即卷积核(滤波矩阵)每个元素赋值
model.at<double>(0, 0) = -4;
model.at<double>(0, 1) = 0;
model.at<double>(0, 2) = 0;

model.at<double>(1, 0) = 0;
model.at<double>(1, 1) = 0;
model.at<double>(1, 2) = 0;

model.at<double>(2, 0) = 0;
model.at<double>(2, 1) = 0;
model.at<double>(2, 2) = 4;

/*
利用自己的算法实现效果,思路如下:
首先,就是对图像的矩阵进行遍历,因为是二维的,所以我们需要一个二重循环
其次,我们要让图像矩阵与卷积核逐个元素相乘求和,那么我们就需要又需要一个二重循环来控制
这里需要注意的是,整个图像的边缘是无法操作的,因为我们是对矩阵中每一个元素周围3*3的范围内进行操作
*/

for (int i = 1; i < image.rows - 1; i++)
{
    for (int j = 1; j < image.cols - 1; j++)
    {
        double sum = 0;  // 加权求和
        for (int m = 0; m < 3; m++)
        {
            for (int n = 0; n < 3; n++)
            {
                sum += ((double)image.at<uchar>(i + m - 1, j + n - 1) * model.at<double>(m, n));
            }
        }
        dimage.at<uchar>(i, j) = sum; // 对dimage矩阵赋值
    }
}
imshow("emboss", dimage);
waitKey(0);

那么,除了浮雕还可不可以实现其他效果呢?

比如下面这些效果


边缘检测

我们更改卷积核(滤波器),使其元素之和为0,如下

/          \
| -1 -1 -1 |
| -1  8 -1 |
| -1 -1 -1 |
\          /

代码更改:

// 给model矩阵,即卷积核(滤波矩阵)每个元素赋值
model.at<double>(0, 0) = -1;
model.at<double>(0, 1) = -1;
model.at<double>(0, 2) = -1;

model.at<double>(1, 0) = -1;
model.at<double>(1, 1) = 8;
model.at<double>(1, 2) = -1;

model.at<double>(2, 0) = -1;
model.at<double>(2, 1) = -1;
model.at<double>(2, 2) = -1;

效果图如下:


锐化

实现的关键还是在于卷积核(滤波器),

如下:

/             \
| -k   -k  -k |
| -k -2k+1 -k |
| -k   -k  -k |
\             /

即,中心比周围多一,也就是找到图像边缘的基础之上,保持图像的亮度

那么将代码中对卷积核(滤波器)的赋值更改一下:

/          \
| -1 -1 -1 |
| -1  9 -1 |
| -1 -1 -1 |
\          /

代码更改

// 给model矩阵,即卷积核(滤波矩阵)每个元素赋值
model.at<double>(0, 0) = -1;
model.at<double>(0, 1) = -1;
model.at<double>(0, 2) = -1;

model.at<double>(1, 0) = -1;
model.at<double>(1, 1) = 9;
model.at<double>(1, 2) = -1;

model.at<double>(2, 0) = -1;
model.at<double>(2, 1) = -1;
model.at<double>(2, 2) = -1;

效果图如下:


高斯模糊

高斯模糊可以有效的给图像降噪,使图像模糊的更加平滑

上图中的上方有两个公式,左边的那个是一维的计算公式,右边的那个是二维的计算公式,这里我需要用到的是二维的计算公式

代码如下:

#include<opencv.hpp>
#include<iostream>
#define PI  3.1415926535
using namespace std;
using namespace cv;
int main()
{
    double sigma = 6;  // 设定sigma值为6,即
    Mat gauss(5, 5, CV_64FC1);  // 创建一个5 * 5的卷积核

    for (int i = 0; i<5; i++)
    {
        for (int j =0; j<5; j++)
        { 
            gauss.at<double>(i, j) = exp(-(i*i + j*j) / (2 * sigma*sigma)) / (2 * PI * sigma * sigma); // 二维计算公式,卷积核元素赋值
        }
    }

    // 归一化处理,目的是让权重之和为1,否则,如果权重之和大于1就太亮,小于1就太暗
    double gssum = sum(gauss).val[0];
    for (int i = 0; i<5; i++)
    {
        for (int j = 0; j<5; j++)
        {
            gauss.at<double>(i, j) /= gssum;
        }
    }


    Mat frame = imread("flower.jpg", 1);  // 读取图片
    cvtColor(frame, frame, CV_RGB2GRAY);  // 将图片转化为灰度图

    //Mat dimg = Mat(frame.rows , frame.cols , CV_8UC1);
    Mat dimg = Mat(frame.rows - 4, frame.cols - 4, CV_8UC1); // 因为原图像的边缘无法处理,这里就创建一个比原图像行列小四的矩阵,当然,用于原图大小相同的矩阵也是可以的

    // 卷积处理,卷积核为5 * 5 的矩阵,而且图像的边缘无法处理,所以这里应该从2开始,行或列-2结束
    for (int i = 2; i < frame.rows - 2; i++) 
    {
        for (int j = 2; j < frame.cols - 2; j++)
        {
            double sum = 0;

            for (int m = 0; m < gauss.rows; m++)
            {
                for (int n = 0; n < gauss.cols; n++)
                {
                    sum += (double)(frame.at<uchar>(i + m - 2, j + n - 2)) * gauss.at<double>(m, n);
                }
            }
            dimg.at<uchar>(i - 2, j - 2) = (uchar)sum;

        }
    }


    imshow("gauss", dimg);   // 显示图片
    waitKey(10);

    system("pause");
    return 0;
}

原图(上图)与效果图(下图)对比情况:


相关API操作

对于刚才的算法实现,其实opencv是有相关api的操作的,下面介绍几个:

高斯模糊

GaussianBlur(image, image, cvSize(5, 5), 10, 10); // 高斯模糊

效果图如下:

跟我实现的差不多

Canny算子

Canny(image, image, 100, 100);  // candy算子

效果图如下:

Sobel算子

Sobel(image, image, 0, 1, 1); // sobel 算子

效果图如下:

代码如下

    Mat image = imread("flower.jpg", 1);  // 读取图片
    cvtColor(image, image, CV_RGB2GRAY); // 转换为灰度图

    GaussianBlur(image, image, cvSize(5, 5), 10, 10); // 高斯模糊

    // Canny(image, image, 100, 100);  // candy算子

    //Sobel(image, image, 0, 1, 1); // sobel 算子

    imshow("api", image);  // 显示图片
    waitKey(0);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值