OpenCV2学习笔记(一):图像的基本操作

一直没有一个系统的时间来深入学习OpenCV,鉴于项目需要,记录一下一些要点以供日后查阅。

OpenCV是一个基于(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac OS操作系统上。其1.0版本于2006年面世,而在2009年又发布了重要的版本:OpenCV2,带来了新的C++接口;现在,OpenCV3也发布了,据说其Python接口大大增强、并且加入了Python 3.x的支持,还带来了许多新的提升,不过这并不在这里的讨论范围之内。这里使用的是:OpenCV2.4.9+Qt5.3.2。

OpenCV库自2.2版本起就被划分成多个模块,在进行开发之前,需要将这些模块编译成库文件,然后在lib文件夹中找到这些模块:

opencv_core模块:其中包含OpenCV基本数据结构、动态数据结构、绘图与数组操作的相关函数、辅助功能与系统函数、基本的算法函数等核心功能。

opencv_improc模块:包含图像处理函数,主要包含图像滤波、图像的几何变换、直方图、特征检测、目标跟踪等内容。

opencv_highgui模块:高层GUI图形用户界面,包含媒体的I/O输入输出函数,读写图像及视频的函数,以及操作图形用户界面函数。

opencv_features2d模块:即2D功能框架,包含兴趣点检测子,描述子以及兴趣点匹配框架。

opencv_calib3d模块:Calibration(校准)加3D这两个词的组合缩写。这个模块主要是相机校准和三维重建相关的内容,包含相机标定,双目几何估计,物体姿态估计以及立体视觉等函数。

opencv_video模块:包含运动估算,特征跟踪以及前景提取函数与相关的类。

opencv_objdetect模块:主要由级联分类(Cascade Classification)和Latent SVM这两个部分。其中包括物体检测函数,如脸部和行人检测。

opencv_ml模块:机器学习模块,主要包括统计模型 (Statistical Models)、一般贝叶斯分类器 (Normal Bayes Classifier)、K-近邻 (K-NearestNeighbors)、支持向量机 (Support Vector Machines)、决策树 (Decision Trees)、提升(Boosting)、梯度提高树(Gradient Boosted Trees)、随机树 (Random Trees)、超随机树 (Extremely randomized trees)、期望最大化 (Expectation Maximization)、神经网络 (Neural Networks)等内容。

opencv_flann模块:高维的近似近邻快速搜索算法库, 主要由两个部分组成:快速近似最近邻搜索和聚类。

opencv_contrib模块:第三方代码,包括一些新添加的不太稳定的可选功能,如新型的人脸识别、立体匹配、人工视网膜模型等技术。

这些模块都对有一个单独的头文件(位于include文件夹)。在Qt中推荐的声明方式如下:

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

而在Qt中,为了使程序能通过编译,必须指定OpenCV的库文件和头文件的路径。因此在创建Qt工程之后,需要在后缀为.pro的项目文件中添加用于构建OpenCV的应用信息:

INCLUDEPATH+=C:\OpenCV\install\include\opencvC:\OpenCV\install\include\opencv2C:\OpenCV\install\include

LIBS+=C:\OpenCV\lib\libopencv_calib3d249.dll.aC:\OpenCV\lib\libopencv_contrib249.dll.aC:\OpenCV\lib\libopencv_core249.dll.aC:\OpenCV\lib\libopencv_features2d249.dll.aC:\OpenCV\lib\libopencv_flann249.dll.aC:\OpenCV\lib\libopencv_gpu249.dll.aC:\OpenCV\lib\libopencv_highgui249.dll.aC:\OpenCV\lib\libopencv_imgproc249.dll.aC:\OpenCV\lib\libopencv_legacy249.dll.aC:\OpenCV\lib\libopencv_ml249.dll.aC:\OpenCV\lib\libopencv_nonfree249.dll.aC:\OpenCV\lib\libopencv_objdetect249.dll.aC:\OpenCV\lib\libopencv_ocl249.dll.aC:\OpenCV\lib\libopencv_video249.dll.aC:\OpenCV\lib\libopencv_photo249.dll.aC:\OpenCV\lib\libopencv_stitching249.dll.aC:\OpenCV\lib\libopencv_superres249.dll.aC:\OpenCV\lib\libopencv_ts249.aC:\OpenCV\lib\libopencv_videostab249.dll.a

二、读取、显示图片并对图片进行简单的阈值分割

这里使用Qt设计一个简单窗口,如图1所示,该窗口包含打开图像和处理图像这两个按钮,同时在下方显示原图像和处理后的图像。

首先创建一个类,主要实现图像的阈值分割:

colordetector.h:

#ifndef COLORDETECTOR_H
#define COLORDETECTOR_H
#include <QFileDialog>
#include <QMainWindow>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

class ColorDetector
{
public:
    // 最小可接受距离
    int minDist;
    void setTargetColor(unsigned char red, unsigned char green, unsigned char blue);
    cv::Mat process(const cv::Mat &image);

private:
    // 目标颜色
    cv::Vec3b target;
    // 输出结果图像
    cv::Mat result;
    void setTargetColor(cv::Vec3b color);
    void colorDetector(const cv::Mat &image, cv::Mat &result);
    void setColorDistanceThreshold(int distance);
    int getColorDistanceThreshold();
    int getDistance(const cv::Vec3b& color);
    cv::Vec3b getTargetColor() const;

};

#endif // COLORDETECTOR_H

colordetector.cpp:

#include "colordetector.h"

int ColorDetector::getDistance(const cv::Vec3b& color)
{
    return abs(color[0] - target[0])+ abs(color[1] - target[1]) + abs(color[1] - target[1]);
}

void ColorDetector::colorDetector(const cv::Mat &image, cv::Mat &result)
{
    cv::Mat_<cv::Vec3b>::const_iterator it = image.begin<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::const_iterator itend = image.end<cv::Vec3b>();
    cv::Mat_<uchar>::iterator itout = result.begin<uchar>();
    // 对每个像素进行处理,计算每个像素距离目标颜色的距离
    for(; it<itend; ++it,++itout)
    {
        if(getDistance(*it)<minDist) // 判断距离是否小于最小可接受距离
        {
            *itout = 0;
        }
        else *itout = 255;
    }
}

void ColorDetector::setColorDistanceThreshold(int distance)
{ // 设置色彩距离阈值,阈值必须为正
    if(distance < 0)
        distance = 0;
        minDist = distance;
}

int ColorDetector::getColorDistanceThreshold(){ // 获取色彩距离的阈值
    return minDist;
}

void ColorDetector::setTargetColor(unsigned char red, unsigned char green, unsigned char blue)
{
    // BGR顺序
    target[2] = red;
    target[1] = green;
    target[0] = blue;
}

void ColorDetector::setTargetColor(cv::Vec3b color)
{ // 设置需检测的颜色
    target = color;
}

cv::Vec3b ColorDetector::getTargetColor() const
{ // 获取需检测的颜色
    return target;
}

// Process阈值分割方法的定义
cv::Mat ColorDetector::process(const cv::Mat &image)
{

    result.create(image.rows, image.cols, CV_8U);
    colorDetector(image, result);
    return result;
}

cv::Mat image表示输入图像,result为输出图像,使用迭代器遍历图像的像素点,每个迭代计算当前像素颜色与设定的目标颜色的距离(用函数getDistance()实现),判断是否在minDist所定义的容忍度之内。判断为真,则将当前像素赋值为0(黑色),否则为255(白色)。当然,计算像素点距离的方法有很多,如计算RGB三个分量的欧拉距离。

在这里,提供两种设置目标颜色的方法。分别是设置RGB图像的三个颜色分量,和使用cv::Vec3b来保存颜色值,在函数setTargetColor()中可以看出。

第二步,进入Qt的ui设计模式拖入两个Push Button和两个label,调整布局并修改按钮和label名称,分别右击”Open Image”和”Process”转到槽,编写槽函数:

void MainWindow::on_pushButton_clicked() // Open Image槽函数
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open Image"), ".",
                       tr("Image Files(*.png *.jpg *.jpeg *.bmp)"));
    image = cv::imread(fileName.toLatin1().data());
    if(image.data){
    //cv::namedWindow("Original Image");
    //cv::imshow("Original Image", image);
    cv::Mat imageclone = image.clone();  // 由于要改变颜色通道顺序,要避免原始图像被改变,使用深拷贝
    cv::cvtColor(imageclone, imageclone, CV_BGR2RGB); // 改变颜色通道的顺序

    QImage img1 = QImage((unsigned char*)(imageclone.data), image.cols,
                         image.rows, QImage::Format_RGB888);
        // 显示在label中
    ui -> label ->setPixmap(QPixmap::fromImage(img1));
        // 改变label尺寸以自适应图像
    ui -> label ->resize(ui->label->pixmap()->size());
    }
    else
        qDebug() << tr("没有输入图像!");
}
void MainWindow::on_pushButton_2_clicked()
{
    if(image.data)
    {
    ColorDetector detect;
    // 设置输入参数
    detect.minDist = 100; // 最小可接受距离
    detect.setTargetColor(130,190,130); // 选定的阈值
//    cv::namedWindow("result");
//    cv::imshow("result", detect.process(image));
    image = detect.process(image); // 对图像做阈值分割
    cv::cvtColor(image, image, CV_GRAY2RGB); // 改变颜色通道的顺序

    QImage img = QImage((unsigned char*)(image.data), image.cols, image.rows, QImage::Format_RGB888);
    // 显示在label中
    ui -> label_2 ->setPixmap(QPixmap::fromImage(img));
    // 改变label尺寸以自适应图像
    ui -> label_2 ->resize(ui->label_2->pixmap()->size());
    }
    else
        qDebug() << tr("无输入图像,无法进行阈值分割");
}

opencv2中用于存储图像数据为Mat类型,而在opencv1中使用的是IplImage,其优点在于Mat是一个类,定义的类类型可以自动分配和释放内存空间,而IplImage需手动为其分配和释放内存空间,当图像较多时,可能会造成内存泄露。

在判断图片是否加载成功时使用了image.data,这是一个指向已分配的内存块的指针,当图片没有加载进来为NULL。

最后对main函数进行简单修改即可:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.setWindowTitle("简单的阈值分割");
    w.show();

    return a.exec();
}

结果:

三:图像复制中的深浅拷贝

需要注意的一点小问题,在复制图像时需要考虑是否想更改原图像,这需要牵涉到浅拷贝和深拷贝的概念:

浅拷贝的主要方式:

Mat A;

A = image ; // 第一种方式

Mat B(image); // 第二种方式

这两种方式之所以称为浅拷贝,是因为它们虽然有不同的名称,但是它们指向相同的内存空间。当其中一个图像矩阵发生变化时,另外一个也会发生变化。

深拷贝的主要方式:

Mat A,B;

A = image.clone(); // 第一种方式

image.copyTo(B); // 第二种方式

深拷贝是真正的复制了一个新的图像矩阵,此时image,A,B三者无论谁发生了改变,其他都不受影响。

最后感谢博客:http://blog.csdn.net/lu597203933 给出的一些讲解。

时间: 2024-08-27 21:56:09

OpenCV2学习笔记(一):图像的基本操作的相关文章

OpenCV2学习笔记:图像的读取与显示

1. 图像读取:imread() Mat imread(const string& ?lename, int ?ags=1 ) 参数介绍: filename: 待加载的文件名称. flags: 此标志用来指定被加载图像的颜色类型(color type).这个标志的取值可以有: -- CV_LOAD_IMAGE_ANYDEPTH : 如果设置这个标志的话,如果图像为16位或32位深度的图像,则返回对应深度的图像:否则,将图像转换为8位深度图像再返回. -- CV_LOAD_IMAGE_COLOR

图像编程学习笔记6——图像转置

转置(transpose)是指将x,y坐标对换,图2.2的转置如图2.15所示. 图2.2 图2.15   图2.2的转置 要注意的是,转置和旋转900是有区别的,不信你可以试试:怎么旋转,图2.2也转不出图2.15来.另外,转置后图的宽高对换了.转置的变换矩阵很简单: (2.12) 镜象变换的源代码如下,因为和旋转的那段程序很类似,程序中的注释就简单一些: 由于很多代码和之前的重复,所以只给出主要功能代码(win32 控制台程序) [cpp] view plaincopy void trans

图像编程学习笔记5——图像镜像

以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序. 镜象(mirror)分水平镜象和垂直镜象两种.图2.2的水平镜象和垂直镜象分别如图2.13和图2.14所示 图2.13   图2.2的水平镜象 图2.14   图2.2的垂直镜象 镜象的变换矩阵很简单.设原图宽为w,高为h,变换后,图的宽和高不变. 水平镜象的变化矩阵为: (2.10) 垂直镜象的变化矩阵为: (2.11) 镜象变换的源代码如下,因为和平移的那段程序很类似,程序中的注释

图像编程学习笔记7——图像缩放

假设放大因子为ratio,(为了避免新图过大或过小,我们在程序中限制0.25≤ratio≤4),缩放(zoom)的变换矩阵很简单: (2.13) 缩放变换的源代码如下,因为和转置的那段程序很类似,程序中的注释就简单一些. [cpp] view plaincopy /** * 函数名: zoom * 参  数: ratio -- 缩放率 * 功  能: 对图片进行水平和垂直镜像操作 *         只保存原图大小的图像数据,如果没有就用白色填充 */ void zoom(double rati

图像编程学习笔记8——图像的平滑(去噪)

第一种方法:高斯模版 以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序. 先举个例子说明一下什么是平滑(smoothing),如下面两幅图所示:可以看到,图3.2比图3.1柔和一些(也模糊一些).是不是觉得很神奇?其实实现起来很简单.我们将原图中的每一点的灰度和它周围八个点的灰度相加,然后除以9,作为新图中对应点的灰度,就能实现上面的效果. 这么做并非瞎蒙,而是有其道理的.大概想一想,也很容易明白.举个例子,就象和面一样,先在中间加点水

图像编程学习笔记9——图像的锐化

锐化(sharpening)和平滑恰恰相反,它是通过增强高频分量来减少图象中的模糊,因此又称为高通滤波(high passfilter).锐化处理在增强图象边缘的同时增加了图象的噪声. 常用的锐化模板是拉普拉斯(Laplacian)模板(见(3.4)式),又是个数学家的名字,可见学好数学,走遍天下都不怕. (3.4) 容易看出拉普拉斯模板的作法:先将自身与周围的8个象素相减,表示自身与周围象素的差别:再将这个差别加上自身作为新象素的灰度.可见,如果一片暗区出现了一个亮点,那么锐化处理的结果是这个

OpenCV2学习笔记(五):图像滤波基础

一:基本概念 滤波是数字图像处理中的一个基本操作,在信号处理领域可以说无处不在.图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,通常是数字图像处理中不可缺少的操作,其处理效果的好坏将直接影响到后续运算和分析的效果.简单来说,图像滤波的根本目的是在图像中提取出人类感兴趣的特征. 当我们观察一幅图像时,有两种处理方法: 1. 观察不同的灰度(或彩色值)在图像中的分布情况,即空间分布. 2. 观察图像中的灰度(或彩色值)的变化情况,这涉及到频率方面的问题. 因此,图像滤波分为频域和

OpenCV2学习笔记(十三):利用SURF匹配不同图像的特征点

SURF算法是著名的尺度不变特征检测器SIFT(Scale-Invariant Features Transform)的高效变种,它为每个检测到的特征定义了位置和尺度,其中尺度的值可用于定义围绕特征点的窗口大小,使得每个特征点都与众不同.这里便是使用SURF算法提取两幅图像中的特征点描述子,并调用OpenCV中的函数进行匹配,最后输出一个可视化的结果,开发平台为Qt5.3.2+OpenCV2.4.9.以下给出图像匹配的实现步骤: 一.输入两幅图像,使用OpenCV中的cv::FeatureDet

OpenCV2学习笔记(六):检测图像颜色小程序

设计一个界面,用来检测一幅图像的颜色分布,开发平台为Qt5.3.2+OpenCV2.4.9. 该程序的主要步骤如下: 1. 载入图像,选定一种颜色: 2. 设定阈值,在该值范围内判定像素属于预设的颜色: 3. 在界面的Label中输出结果. 首先,新建一个Qt Widgets Application,其中基类选择为QWidget,在创建完项目后,添加一个检测图像颜色的类ColorDetector.并在在Qt项目的.pro文件中添加: INCLUDEPATH+=C:\OpenCV\install\