2.opencv图像处理常用操作

作者: 在河之简 | 来源:发表于2015-05-11 23:01 被阅读1903次

    图像的平滑处理

    平滑,也称 模糊, 平滑处理时需要用到一个滤波器 。滤波器想象成一个包含加权系数的窗口,这个加权系数也叫做核或者模版。

        // 图像平滑处理分而学之.cpp : 定义控制台应用程序的入口点。
        //
        #include "stdafx.h"
        #include <iostream>
        #include <opencv2/opencv.hpp>
        using namespace std;
        using namespace cv;
        const int MAX_KERNEL_LENGTH = 31;
        int _tmain(int argc, _TCHAR* argv[])
        {
            Mat img = imread("D:\\lenargb.jpg", 1);
            if (img.empty())
            {
                cout << "无法读入图像" << endl;
                return -1;
            }
            Mat dest;
        
        #pragma region 归一化平滑
            for (int i = 1; i < MAX_KERNEL_LENGTH; i++)
            {
                blur(img, dest, Size(i, i), Point(-1, -1));//size(i,i)内核大小,最小为(1,1);
                imshow("归一化平滑图像", dest);
                waitKey(100);
        
            }
        #pragma endregion
        #pragma region 高斯平滑
            for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
            {
                GaussianBlur(img, dest, Size(i, i), 0, 0);//size的两个参数必须都为正奇数,第四个参数是x方向的标准差,第五个参数是y方向的标准差,如果是0,表示从内核大小计算得到;
                imshow("高斯平滑图像图像", dest);
                waitKey(100);
            }
        #pragma endregion
        
        #pragma region 中值平滑
            for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
            {
                medianBlur(img, dest, i);//第三个参数为核的边长,必须为奇数,一般中值平滑用的都是正方形所以只用一个参数就好;
                imshow("中值平滑", dest);
                waitKey(100);
            }
        #pragma endregion
        
            //waitKey(0);
            return 0;
        }
    

    图像阈值操作

    为了从一副图像中提取出我们需要的部分,应该用图像中的每一个像素点的灰度值与选取的阈值进行比较,并作出相应的判断。
    一旦找到了需要分割的物体的像素点,我们可以对这些像素点设定一些特定的值来表示。(例如:可以将该物体的像素点的灰度值设定为:‘0’(黑色),其他的像素点的灰度值为:‘255’(白色);
    OpenCV中提供了阈值(threshold)函数 有五种类型

    1. 二进制阈值化
      先要选定一个特定的阈值量,比如:120,这样,新的阈值产生规则可以解释为大于120的像素点的灰度值设定为最大值(如8位灰度值最大为255),灰度值小于120的像素点的灰度值设定为0。
    2. 反二进制阈值化
      该阈值化与二进制阈值化相似,先选定一个特定的灰度值作为阈值,不过最后的设定值相反。
    3. 截断阈值化
      同样首先需要选定一个阈值,图像中大于该阈值的像素点被设定为该阈值,小于该阈值的保持不变
    4. 阈值化为0
      首先需要选定一个阈值,像素点的灰度值大于该阈值的不进行任何改变;2 像素点的灰度值小于该阈值的,其灰度值全部变为0
    5. 反阈值化为0
      原理类似于0阈值,但是在对图像做处理的时候相反,即:像素点的灰度值小于该阈值的不进行任何改变,而大于该阈值的部分,其灰度值全部变为0。
      filter2D函数能够对图像按照模版进行滤波
        // 基本阈值操作2.cpp : 定义控制台应用程序的入口点。
        //
        
        #include "stdafx.h"
        #include <iostream>
        #include <opencv2/opencv.hpp>
        using namespace  std;
        using namespace cv;
        
        
        void respond(int, void*);
        const char * window_name = "图片";
        Mat src_gray;
        Mat dst;
        int threshhold_type = 5;
        const int max_type = 5;
        int threshhold_value = 0;
        int max_value = 255;
        int _tmain(int argc, _TCHAR* argv[])
        {
            namedWindow(window_name, WINDOW_AUTOSIZE);
            Mat src = imread("E:\\code\\test\\image\\tiantan.png", 1);
            if (src.empty())
            {
                cout << "无法正常载入图片" << endl; return -1;
            }
            //转换为灰度图;
            cvtColor(src, src_gray, CV_RGB2GRAY);
            imshow(window_name, src_gray);
            createTrackbar("阈值类型", window_name, &threshhold_type, max_type, respond);
            createTrackbar("阈值大小", window_name, &threshhold_value, max_value, respond);
            waitKey(0);
            return 0;
        }
        void respond(int, void*)
        {
            /* 0:二进制阈值
            1: 反二进制阈值
            2: 截断阈值
            3: 0阈值
            4: 反0阈值
            5:原灰度图;
            */
            if (threshhold_type==5)
            {
                imshow(window_name, src_gray);
            }
            else
            {
                threshold(src_gray, dst, threshhold_value, max_value, threshhold_type);
                imshow(window_name, dst);
            }
        }
    

    实现自己的线性滤波器

    OpenCV为我们提供了函数 filter2D ,来实现核卷积;

        // 实现自己的滤波器2.cpp : 定义控制台应用程序的入口点。
        //
    
        #include "stdafx.h"
        #include <iostream>
        #include <opencv2/opencv.hpp>
        using namespace std;
        using namespace cv;
        void respond(int, void*);
        const char * window_name = "实现自己的滤波器";
        int value = 0;
        Mat src,dst,kernel;
        const int max_value = 100;
        
        int _tmain(int argc, _TCHAR* argv[])
        {
            src = imread("E:\\code\\test\\image\\lena.png", 1);
            if (src.data==NULL)
            {
                cout << "无法加载图片" << endl;
                return -1;
            }
            namedWindow(window_name, WINDOW_AUTOSIZE);
            imshow(window_name, src);
            createTrackbar("核的大小", window_name, &value, max_value, respond);
            while (true)
            {
                
                char c = waitKey(0);
                if (c==27)
                {
                    return 0;
                }
            }
        
            return 0;
        }
        
        void respond(int, void*)
        {
            int kernel_size = 1 + value * 2;
            kernel = Mat::ones(kernel_size, kernel_size, CV_32F)/(float)(kernel_size*kernel_size);
            filter2D(src, dst, -1, kernel, Point(-1, -1));
            imshow(window_name, dst);
        }
    

    Sobel导数

    Sobel 算子是一个离散微分算子 (discrete differentiation operator)。 它用来计算图像灰度函数的近似梯度。Sobel 算子结合了高斯平滑和微分求导。

        // Sobel导数.cpp : 定义控制台应用程序的入口点。
        //
        #include "stdafx.h"
        
        #include "opencv2/imgproc/imgproc.hpp"
        #include "opencv2/highgui/highgui.hpp"
        #include <stdlib.h>
        #include <stdio.h>
        
        using namespace cv;
        
        /** @function main */
        int main(int argc, char** argv)
        {
        
            Mat src, src_gray;
            Mat grad;
            char* window_name = "Sobel Demo - Simple Edge Detector";
            int scale = 1;
            int delta = 0;
            int ddepth = CV_16S;
        
            //int c;
        
            /// 装载图像
            src = imread("E://code//test//image//lena.png",1);
        
            if (!src.data)
            {
                return -1;
            }
            
            GaussianBlur(src, src, Size(3, 3), 0, 0, BORDER_DEFAULT);
            /// 创建显示窗口
            namedWindow(window_name, CV_WINDOW_AUTOSIZE);
            imshow(window_name, src);
            waitKey(3000);
            /// 转换为灰度图
            cvtColor(src, src_gray, CV_RGB2GRAY);
            
        
            imshow(window_name, src_gray);
            waitKey(3000);
            /// 创建 grad_x 和 grad_y 矩阵
            Mat grad_x, grad_y;
            Mat abs_grad_x, abs_grad_y;
        
            /// 求 X方向梯度
            
            Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
            //Sobel(src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT);
            convertScaleAbs(grad_x, abs_grad_x);
        
            /// 求Y方向梯度
            Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
            //Sobel(src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT);
            convertScaleAbs(grad_y, abs_grad_y);
            
            /// 合并梯度(近似)
            
            addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad);
        
            imshow(window_name, grad);
        
            waitKey(0);
        
            return 0;
        }
    
    //其实不简单也没有关系,因为只是相对大小,在图像的对比中,依然能够找到边界;
    

    Laplace 算子

    一阶导数的极值位置,二阶导数为0。所以我们也可以用这个特点来作为检测图像边缘的方法。 但是, 二阶导数的0值不仅仅出现在边缘(它们也可能出现在无意义的位置),但是我们可以过滤掉这些点。
    实际上,由于 Laplacian使用了图像梯度,它内部调用了 Sobel 算子。

        // Laplace算子.cpp : 定义控制台应用程序的入口点。
        //
        
        #include "stdafx.h"
        
        #include "opencv2/imgproc/imgproc.hpp"
        #include "opencv2/highgui/highgui.hpp"
        #include <stdlib.h>
        #include <stdio.h>
        
        using namespace cv;
        
        /** @函数 main */
        int main(int argc, char** argv)
        {
            Mat src, src_gray, dst;
            int kernel_size = 3;
            int scale = 1;
            int delta = 0;
            int ddepth = CV_16S;
            char* window_name = "Laplace Demo";
        
            int c;
        
            /// 装载图像
            src = imread("E://code//test//image//lena.png", 1);
        
            if (!src.data)
            {
                return -1;
            }
        
            /// 使用高斯滤波消除噪声
            GaussianBlur(src, src, Size(3, 3), 0, 0, BORDER_DEFAULT);
        
            /// 转换为灰度图
            cvtColor(src, src_gray, CV_RGB2GRAY);
        
            /// 创建显示窗口
            namedWindow(window_name, CV_WINDOW_AUTOSIZE);
            imshow(window_name, src);
            waitKey(2000);
            imshow(window_name, src_gray);
            waitKey(3000);
        
            /// 使用Laplace函数
            Mat abs_dst;
        
            Laplacian(src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT);
            //ddepth: 输出图像的深度。 因为输入图像的深度是 CV_8U ,这里我们必须定义 ddepth = CV_16S 以避免外溢。
            //下面大概求绝对值;
            convertScaleAbs(dst, abs_dst);
        
            /// 显示结果
            imshow(window_name, abs_dst);
            
            waitKey(0);
        
            return 0;
        }
    

    Canny 边缘检测

    步骤

    1. 消除噪声。 使用高斯平滑滤波器卷积降噪。
    2. 计算梯度幅值和方向。 此处,使用Sobel滤波器
    3. 非极大值 抑制。 这一步排除非边缘像素, 仅仅保留了一些细线条(候选边缘)。
    4. 滞后阈值: 最后一步,Canny 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值): Canny 推荐的 高:低 阈值比在 2:1 到3:1之间。
        // Canny边缘检测.cpp : 定义控制台应用程序的入口点。
        //
        
        #include "stdafx.h" 
        #include "opencv2/imgproc/imgproc.hpp"
        #include "opencv2/highgui/highgui.hpp"
        #include <stdlib.h>
        #include <stdio.h>
        
        using namespace cv;
        
        /// 全局变量
        
        Mat src, src_gray;
        Mat dst, detected_edges;
        
        //int edgeThresh = 1;
        int lowThreshold=1;
        int const max_lowThreshold = 100;
        int ratio = 3;
        int kernel_size = 3;
        char* window_name = "Edge Map";
        
        /**
        * @函数 CannyThreshold
        * @简介: trackbar 交互回调 - Canny阈值输入比例1:3
        */
        void CannyThreshold(int, void*)
        {
            /// 使用 3x3内核降噪
            blur(src_gray, detected_edges, Size(3, 3));
        
            /// 运行Canny算子
            Canny(detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size);
            //第一个参数是原图像,第二个参数是输出图像,支持本地计算,第三个参数是低阈值,第四个参数是高阈值,第五个参数是内部sobel算子使用的内核大小。
            /// 使用 Canny算子输出边缘作为掩码显示原图像
            dst = Scalar::all(0);
            //把dst填充为黑色
            src.copyTo(dst, detected_edges);
            //第一个参数为输出图像,第二个参数为掩码;即把第二个图像中非0的部分在src中的像素复制给dst;
        
            imshow(window_name, dst);
        }
        
        
        /** @函数 main */
        int main(int argc, char** argv)
        {
            /// 装载图像
            src = imread("E:\\code\\test\\image\\lena.png",1);
        
            if (!src.data)
            {
                return -1;
            }
        
            /// 创建与src同类型和大小的矩阵(dst)
            dst.create(src.size(), src.type());
        
            /// 原图像转换为灰度图像
            cvtColor(src, src_gray, CV_BGR2GRAY);
        
            /// 创建显示窗口
            namedWindow(window_name, CV_WINDOW_AUTOSIZE);
        
            /// 创建trackbar
            createTrackbar("Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold);
        
            /// 显示图像
            CannyThreshold(0, 0);
        
            /// 等待用户反应
            waitKey(0);
        
            return 0;
        }
    
    1.png
        /// 使用 3x3内核降噪
        blur(src_gray, detected_edges, Size(3, 3));
    
        /// 运行Canny算子
        Canny(detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size);
        //第一个参数是原图像,第二个参数是输出图像,支持本地计算,第三个参数是低阈值,第四个参数是高阈值,第五个参数是内部sobel算子使用的内核大小。
        /// 使用 Canny算子输出边缘作为掩码显示原图像
    

    相关文章

      网友评论

        本文标题:2.opencv图像处理常用操作

        本文链接:https://www.haomeiwen.com/subject/jndfqttx.html