1. 图画的概括

在该系列第三篇文章中,曾经简略地介绍过概括和概括发现。

1.1 概括的基本概念

图画的概括是指图画中具有相同色彩灰度值的连续点的曲线。概括和边际是有联络的,边际是概括的根底,概括是边际的连续调集。

概括和边际的区别是:

  • 概括是连续的,边际可所以连续的,也可所以离散的。
  • 概括是完整的,边际可所以完整的,也可所以不完整的。
  • 概括能够有各种形状,边际通常是线性的。

1.2 概括发现和概括提取

概括发现是指在图画中找到一切可能的概括。

概括提取是指从图画中找到一切有用的概括和概括的具体信息。

概括发现是概括提取的前提,概括提取在概括发现的根底上进一步提取概括的形状和方位信息等等。

下面的代码,经过一系列操作找到二值图画的有用概括后,获取这些概括的最小外接矩形,最终用线在原图中框出这些外接矩形,然后在原图中找到比较显着的苹果。

#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
bool ascendSort(vector<Point> a,vector<Point> b)
{
    return contourArea(a) > contourArea(b);
}
int main(int argc, char **argv) {
    Mat src = imread(".../apple.jpg");
    imshow("src", src);
    Mat hsv,edge;
    cvtColor(src, hsv, cv::COLOR_BGR2HSV); // BGR 转换到 HSV 色彩空间
    imshow("hsv", hsv);
    cv::Scalar lower_red(0, 43, 46);
    cv::Scalar upper_red(10, 255, 255); // 界说红色的 HSV 规模
    Mat mask;
    inRange(hsv, lower_red, upper_red, mask); // 经过 inRange 函数实现二值化
    imshow("mask", mask);
    Mat kernel = getStructuringElement(MORPH_RECT, Size(15, 15));
    morphologyEx(mask, mask, MORPH_CLOSE, kernel); // 形态学操作
    morphologyEx(mask, mask, MORPH_OPEN, kernel);  // 形态学操作
    imshow("morphology", mask);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    sort(contours.begin(), contours.end(), ascendSort);//ascending sort
    for (size_t i = 0; i< contours.size(); i++) {
        double area = contourArea(contours[i]);
        if (area < 22000) {
            continue;
        }
        cout << "area = " << area << endl;
        RotatedRect rrt = minAreaRect(contours[i]);
        Point2f pt[4];
        rrt.points(pt);
        line(src, pt[0], pt[1], Scalar(255, 0, 0), 8, 8);
        line(src, pt[1], pt[2], Scalar(255, 0, 0), 8, 8);
        line(src, pt[2], pt[3], Scalar(255, 0, 0), 8, 8);
        line(src, pt[3], pt[0], Scalar(255, 0, 0), 8, 8);
    }
    imshow("result", src);
    waitKey(0);
    return 0;
}

展示原图

OpenCV 笔记(14):图画的概括和概括的根底特征

将原图转换成 HSV 类型,用于提取特定色彩。

OpenCV 笔记(14):图画的概括和概括的根底特征

经过 inRange 函数实现二值化。inRange 函数用于将图画中的像素值约束在指定的规模内,它会将满意条件的像素设置为 255,不满意条件的像素设置为 0,然后形成一个二值图画。

OpenCV 笔记(14):图画的概括和概括的根底特征

对二值图画进行一些形态学的操作,便于后续的概括分析。

OpenCV 笔记(14):图画的概括和概括的根底特征

经过 findContours() 函数进行概括发现。最终,筛选出有用的概括,并获取最小外接矩形,用线画出在原图上展示出来。

OpenCV 笔记(14):图画的概括和概括的根底特征

2. 概括特征的分类

图画的概括特征能够分为以下几类:

  • 根底特征:面积、周长、质心、凸包、最小外接矩形等。这些特征能够直接从概括序列上钩算得到。

  • 矩特征:Hu 矩、中心矩、惯性矩等。这些特征能够用于描述概括的形状和巨细。

  • 几许特征:最小闭合圆、拟合椭圆等。这些特征能够用于描述概括的几许形状。

3. 概括的根底特征

3.1 面积、周长、最小外接矩形

  • 概括面积 contourArea()
  • 概括周长 arcLength()
  • 概括外接矩形 boundingRect()
  • 概括最小外接矩形 minAreaRect()

下面的比如获取图中回形针的概括,以及概括的面积、周长、最小外接矩形等。

#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
bool ascendSort(vector<Point> a,vector<Point> b)
{
    return contourArea(a) > contourArea(b);
}
int main(int argc, char **argv) {
    Mat src = imread(".../paperclip.jpg");
    imshow("src", src);
    Mat gray,thresh;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    imshow("gray", gray);
    threshold(gray,thresh,0,255,THRESH_BINARY_INV | THRESH_OTSU);
    imshow("thresh", thresh);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(thresh, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    sort(contours.begin(), contours.end(), ascendSort);//ascending sort
    for (size_t i = 0; i< contours.size(); i++) {
        double area = contourArea(contours[i]);
        double length = arcLength(contours[i],true);
        if (area < 1000) {
            continue;
        }
        cout << "area = " << area << ", length = " << length << endl;
        RotatedRect rrt = minAreaRect(contours[i]);// 获取最小外接矩形
        Point2f pt[4];
        rrt.points(pt);
        line(src, pt[0], pt[1], Scalar(255, 0, 0), 8, 8);
        line(src, pt[1], pt[2], Scalar(255, 0, 0), 8, 8);
        line(src, pt[2], pt[3], Scalar(255, 0, 0), 8, 8);
        line(src, pt[3], pt[0], Scalar(255, 0, 0), 8, 8);
        Point  center = rrt.center;
        circle(src, center, 2,Scalar(0, 0, 255), 8, 8); // 制作最小外接矩形的中心点
    }
    imshow("result", src);
    waitKey(0);
    return 0;
}

履行成果:

area = 101573, length = 2461.71
area = 41757.5, length = 1256.08
area = 41348, length = 1152.56
area = 39717.5, length = 1616.13
area = 37503, length = 1230.47
area = 36742.5, length = 1037.21
area = 4142, length = 706.357

OpenCV 笔记(14):图画的概括和概括的根底特征

外接矩形是指能够围住概括一切点的矩形,而最小外接矩形是指包括概括中一切点的最小矩形。

下面的比如,获取图中最大概括的外接矩形和最小外接矩形,分别用黄色和蓝色表示。

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
bool ascendSort(vector<Point> a,vector<Point> b)
{
    return contourArea(a) > contourArea(b);
}
int main(int argc, char **argv) {
    Mat src = imread(".../fruit.jpg");
    imshow("src", src);
    Mat gray,thresh;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    threshold(gray,thresh,0,255,THRESH_BINARY | THRESH_OTSU);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(thresh, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    sort(contours.begin(), contours.end(), ascendSort);//ascending sort
    RotatedRect rrt = minAreaRect(contours[0]);// 获取最大概括的最小外接矩形
    Point2f pt[4];
    rrt.points(pt);
    line(src, pt[0], pt[1], Scalar(255, 0, 0), 8, 8);
    line(src, pt[1], pt[2], Scalar(255, 0, 0), 8, 8);
    line(src, pt[2], pt[3], Scalar(255, 0, 0), 8, 8);
    line(src, pt[3], pt[0], Scalar(255, 0, 0), 8, 8);
    Rect rect = boundingRect(contours[0]);// 获取最大概括的外接矩形
    rectangle(src,rect,Scalar(0, 255, 255), 8, 8);// 制作外接矩形
    imshow("result", src);
    waitKey(0);
    return 0;
}

OpenCV 笔记(14):图画的概括和概括的根底特征

经过上述比如能够看到,最小外接矩形能够更准确地描述概括的形状和巨细。

外接矩形和最小外接矩形有各自的运用场景,例如在对象检测中,能够运用外接矩形来粗略定位物体,而运用最小外接矩形来准确定位物体。

3.2 凸包

凸包(Convex Hull)是计算几许(图形学)中的概念。在一个实数向量空间 V 中,关于给定调集 X,一切包括 X 的凸集的交集 S 被称为 X 的 凸包。

二维欧几里得空间中,凸包可幻想为一条刚好包著一切点的橡皮圈。

  • 平面的一个子集 S 被称为是“凸”的,当且仅当关于任意两点 p,s ∈S,线段 ps 都完全归于S。
  • 一个点集 P 的凸包CH(P),就是包括 P 的最小凸集——即包括P的一切凸集的交。

凸包的性质:

  • 凸包是凸集。
  • 凸包的周长是最小的。
  • 凸包的面积是最小的。
  • 凸包的质心是一切点的质心的均值。

OpenCV 提供了 convexHull() 函数寻找概括的凸包以及 isContourConvex() 函数用于判断概括是否为凸概括。凸概括是指一切内角都小于或等于 180 度的概括。

#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
int main(int argc, char **argv) {
    Mat src = imread(".../hand.jpg");
    imshow("src", src);
    Mat gray,thresh;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    threshold(gray,thresh,0,255,THRESH_BINARY_INV | THRESH_OTSU);
    imshow("thresh", thresh);
    Mat mask;
    Mat kernel = getStructuringElement(MORPH_RECT, Size(31, 31));
    morphologyEx(thresh, mask, MORPH_CLOSE, kernel); // 形态学操作
    imshow("morphology", mask);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    vector<vector<Point>> hull(contours.size());
    Mat drawing = Mat::zeros(mask.size(),CV_8UC3);
    for (size_t i = 0; i< contours.size(); i++) {
        double area = contourArea(contours[i]);
        if (area < 100) {
            continue;
        }
        convexHull(contours[i], hull[i], false);
        bool isHull = isContourConvex(contours[i]);
        cout << "isHull = " << isHull << endl;
        drawContours(drawing, contours, i, Scalar(0, 0, 255), 8, 8);
        drawContours(drawing, hull, i, Scalar(255, 0, 0), 8, 8);
        drawContours(src, hull, i, Scalar(255, 0, 0), 8, 8);
    }
    imshow("result",src);
    imshow("drawing",drawing);
    waitKey(0);
    return 0;
}

履行成果:

isHull = 0
isHull = 0

OpenCV 笔记(14):图画的概括和概括的根底特征

OpenCV 笔记(14):图画的概括和概括的根底特征

4. 总结

概括的根底特征是计算机视觉中的重要工具,这些特征能够应用于对象检测、形状识别、测量等各种应用场景。后续还会介绍更多的概括特征。