码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-02-08 23:21:28      阅读:439      评论:0      收藏:0      [点我收藏+]

标签:qt   opencv   阈值分割   图像处理   c   

一直没有一个系统的时间来深入学习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 给出的一些讲解。

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

标签:qt   opencv   阈值分割   图像处理   c   

原文地址:http://blog.csdn.net/liyuefeilong/article/details/43628399

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!