OpenCV图像处理教程C++(二十一)图像矩、点多边形测试

图像矩:
矩:主要指几何矩 Mji = ∑ (P(x,y) · x^j · y^i) 其中 (i+j) 和等于几就是几阶矩,
P(x,y)表示图像x,y坐标位置的像素值,i j 为变量x,y
还有中心矩(MUji), 中心归一化矩(NUji)

图像中心 Center(x0, y0) x0 = M10 / M00 , y0 = M01 / M00
此非图像坐标的中心点,而是图像 质 的中心,质点
API:

moments( // 计算矩,返回 0阶到3阶所有的几何矩,2阶到3阶所有的中心矩,2阶到3阶所有的中心归一矩
InputArray  array, // 输入数据,findContours 找到的轮廓数据
bool   binaryImage=false // 是否为二值图像
)
contourArea( // 求轮廓面积
InputArray  contour, // 输入轮廓数据
bool   oriented // 默认false、返回绝对值
)

arcLength( // 求轮廓弧长
InputArray  curve, // 输入曲线数据
bool   closed // 是否是封闭曲线
)

步骤

  • 提取图像边缘
  • 发现轮廓
  • 计算每个轮廓对象的矩
  • 计算每个对象的中心、弧长、面积

    代码:

#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 
using namespace cv;
using namespace std;

 Mat src, gray_src,dst;
 int threshold_value = 175;
 int threshold_max = 255;
 RNG rng(12345);
void get_moments(int, void*)
{
    Mat canny_output;
    vector<vector<Point>> contours;
    //vector<Vec4i> hierachy;

    Canny(gray_src, canny_output, threshold_value, threshold_value * 2, 3, false);//提取边缘
    findContours(canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);//发现轮廓

    vector<Moments> contours_moments(contours.size());//存放各阶各种矩
    vector<Point2f> ccs(contours.size());//各阶各种矩的图像中心,质点
    for (size_t i = 0; i < contours.size(); i++) {
        //计算矩,返回 0阶到3阶所有的几何矩,2阶到3阶所有的中心矩,2阶到3阶所有的中心归一矩
        contours_moments[i] = moments(contours[i]);
        ccs[i] = Point(static_cast<float>(contours_moments[i].m10 / contours_moments[i].m00),
            static_cast<float>(contours_moments[i].m01 / contours_moments[i].m00));//计算质点
    }
    src.copyTo(dst);
    for (size_t i = 0; i < contours.size(); i++) {
        if (contours[i].size() < 100) { // 点数小于100的轮廓,忽略
            continue;
        }
        Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
        printf("center point x=%.2f, y=%.2f\n", ccs[i].x, ccs[i].y);
        printf("contours %d area=%.2f, arc length=%.2f\n", i, contourArea(contours[i]),
            arcLength(contours[i], true));
        drawContours(dst, contours, i, color);//绘制轮廓
        circle(dst, ccs[i], 2, color);//绘制质点
    }

    imshow("dst", dst);
}
int main()
{
    src = imread("C:\\Users\\Administrator\\Desktop\\pic\\6.jpeg");// redball.png
    imshow("src32", src);
    cvtColor(src, gray_src, CV_BGR2GRAY);
    //GaussianBlur(gray_src, gray_src, Size(3, 3), 0, 0);//这里用高斯模糊去噪,相比于均值,轮廓发现的准确
    get_moments(0, 0);
    createTrackbar("rect round:", "dst", &threshold_value, threshold_max, get_moments);
    waitKey(0);
}

结果:
这里写图片描述

点多边形测试
测试一个点是否在给定的多边形内部,边缘,或者外部

pointPolygonTest( // 点多边形测试,返回数据是double类型
InputArray  contour, // 输入的轮廓
Point2f  pt, // 测试点
bool  measureDist // 是否返回距离值,如果是false,1表示在内面,0表示在边界上,-1表示在外部,true返回实际距离(pt和轮廓中与pt坐标位置最近的点的距离?)
)

步骤:

  • 构建一张400x400大小的图片
  • 画一一个六边形的闭合区域line
  • 发现轮廓
  • 对象中所有像素点做点多边形测试,得到距离,归一化后显示

代码:

#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 
using namespace cv;
using namespace std;

int main()
{
    //画6边形
    const int r = 100;
    Mat src = Mat::zeros(r * 4, r * 4, CV_8UC1); //构建一张400x400大小的图片
    vector<Point2f> vert(6); //定义了6个点
    vert[0] = Point(3 * r / 2, static_cast<int>(1.34*r));
    vert[1] = Point(1 * r, 2 * r);
    vert[2] = Point(3 * r / 2, static_cast<int>(2.866*r));
    vert[3] = Point(5 * r / 2, static_cast<int>(2.866*r));
    vert[4] = Point(3 * r, 2 * r);
    vert[5] = Point(5 * r / 2, static_cast<int>(1.34*r));
    for (int i = 0; i < 6; i++)
    {
        line(src, vert[i], vert[(i + 1) % 6], Scalar(255), 3, 8, 0); //画6条线
    }

    //findContours
    vector<vector<Point>> contours;
    vector<Vec4i> hierachy;
    Mat csrc;
    src.copyTo(csrc);
    findContours(csrc, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

    //测试是在轮廓里面还是在轮廓外面
    Mat raw_dist = Mat::zeros(csrc.size(), CV_32FC1); //测试要生成一张图,一张距离的图
    for (int row = 0; row < raw_dist.rows; row++) //对每一个像素做点多边形测试
    {
        for (int col = 0; col < raw_dist.cols; col++)
        { //                                   contour这里只有一个,那个6变形,实际上说再有一个循环,对每一个contour做
            double dist = pointPolygonTest(contours[0], Point2f(static_cast<float>(col), static_cast<float>(row)), true);
            raw_dist.at<float>(row, col) = static_cast<float>(dist);  //得到距离这又设到这里面去
        }
    } //这样就得到了每一个点跟多边形的距离

      //寻找最大最小值
    double minValue, maxValue;
    minMaxLoc(raw_dist, &minValue, &maxValue, 0, 0, Mat()); //一张图像最大最小值的位置在哪

                                                            //根据最大最小值把像素重新算出来然后生成一张图
    Mat drawImg = Mat::zeros(src.size(), CV_8UC3); //这张图是绘制出来的所以叫三通道的RGB的才好交代一点
    for (int row = 0; row < drawImg.rows; row++)
    {
        for (int col = 0; col < drawImg.cols; col++)
        {
            float dist = raw_dist.at<float>(row, col); //对每一个像素获取这个值
            if (dist > 0) //表示在多边形的内部
            {                                    // blue通道
                drawImg.at<Vec3b>(row, col)[0] = (uchar)(abs(1.0 - (dist / maxValue)) * 255); //(dist / maxValue)) * 255 确保它的值在0-255之间,因为它最大也不大过最大值
            }
            else if (dist < 0)  //在多边形的外部
            {
                drawImg.at<Vec3b>(row, col)[2] = (uchar)(abs(1.0 - (dist / minValue)) * 255);
            }
            else
            {
                drawImg.at<Vec3b>(row, col)[0] = (uchar)(abs(255 - dist));
                drawImg.at<Vec3b>(row, col)[1] = (uchar)(abs(255 - dist));
                drawImg.at<Vec3b>(row, col)[2] = (uchar)(abs(255 - dist));
            }
        }
    }
    imshow("input", src);
    imshow("output", drawImg);

    waitKey(0);
}

结果:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_26907755/article/details/81740517