基本形状识别(openCV C ++)

时间:2018-07-31 11:17:49

标签: c++ opencv visual-c++

我有一个班级需要做的项目,我选择的技能有点超出我的技能。 目标是计数掷骰子的结果。 现在,我正在尝试使其在示例图片上起作用:

sample pic of dices

enter image description here

我的当前代码添加到下面:

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

Mat KostkaFunkcja(Mat image, Mat in, Scalar low, Scalar high);
int getMaxAreaContourId(vector <vector<cv::Point>> contours);
vector<Point> contoursConvexHull(vector<vector<Point> > contours, int index);
Mat ZnakiFunkcja(Mat image, Mat in, Scalar low, Scalar high);


int main(int argc, char** argv)
{
    Mat image;
    image = imread("kostki.jpg", CV_LOAD_IMAGE_COLOR);

    if (!image.data)
    {
        cout << "Could not open or find the image" << std::endl;
        return -1;
    }

    Mat imgHSV;
    Mat workimage = image;

    cvtColor(workimage, imgHSV, COLOR_BGR2HSV); //Convert the captured frame from BGR to HSV
    //red dice
    workimage = KostkaFunkcja(workimage, imgHSV, Scalar(146, 0, 31), Scalar(179, 255, 255));
    //green dice
    workimage = KostkaFunkcja(workimage, imgHSV, Scalar(25, 147, 0), Scalar(98, 255, 154));
    //yellow dice
    workimage = KostkaFunkcja(workimage, imgHSV, Scalar(22, 45, 161), Scalar(91, 255, 255));
    //black dice
    workimage = KostkaFunkcja(workimage, imgHSV, Scalar(98, 0, 0), Scalar(179, 232, 107));
    //white symbols
    workimage = ZnakiFunkcja(workimage, imgHSV, Scalar(58, 0, 183), Scalar(179, 145, 255));
    namedWindow("Kostki_kontur", CV_WINDOW_AUTOSIZE);
    imshow("Kostki_kontur", workimage);
    waitKey(0);

    return 0;

}

Mat KostkaFunkcja(Mat image, Mat in, Scalar low, Scalar high)
{
    Mat temp;
    inRange(in, low, high, temp);
    erode(temp, temp, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
    dilate(temp, temp, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
    dilate(temp, temp, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
    erode(temp, temp, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));

    Mat srcBlur, srcCanny;
    blur(temp, srcBlur, Size(3, 3));

    Canny(srcBlur, srcCanny, 0, 100, 3, true);
    vector<vector<Point> > contours;
    findContours(srcCanny, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    int largest_contour_index = getMaxAreaContourId(contours);

    Mat drawing = Mat::zeros(srcCanny.size(), CV_8UC3);

    for (int i = 0; i< contours.size(); i++)
    {
        Scalar color = Scalar(255, 255, 255);
        drawContours(drawing, contours, i, color, 2);
    }

    vector<Point> ConvexHullPoints = contoursConvexHull(contours, largest_contour_index);
    polylines(image, ConvexHullPoints, true, Scalar(0, 0, 255), 2);
    return image;
}

vector<Point> contoursConvexHull(vector<vector<Point> > contours, int index)
{
    vector<Point> result;
    vector<Point> pts;
    for (size_t j = 0; j< contours[index].size(); j++)
        pts.push_back(contours[index][j]);
    convexHull(pts, result);
    return result;
}

int getMaxAreaContourId(vector <vector<cv::Point>> contours)
{
    double maxArea = 0;
    int maxAreaContourId = -1;
    for (int j = 0; j < contours.size(); j++) {
        double newArea = cv::contourArea(contours.at(j));
        if (newArea > maxArea) {
            maxArea = newArea;
            maxAreaContourId = j;
        }
        return maxAreaContourId;
    }
}
Mat ZnakiFunkcja(Mat image, Mat in, Scalar low, Scalar high)
{
    Mat temp;
    inRange(in, low, high, temp);
    erode(temp, temp, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
    dilate(temp, temp, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
    dilate(temp, temp, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
    erode(temp, temp, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));

    Mat srcBlur, srcCanny;
    blur(temp, srcBlur, Size(3, 3));

    Canny(srcBlur, srcCanny, 0, 100, 3, true);
    vector<vector<Point> > contours;
    findContours(srcCanny, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    Mat drawing = Mat::zeros(srcCanny.size(), CV_8UC3);
    for (int i = 0; i< contours.size(); i++)
    {
        Scalar color = Scalar(255, 255, 255);
        drawContours(drawing, contours, i, color, 2);
        polylines(image, contours, true, Scalar(0, 0, 255), 2);
        return image;
    }

}

但是我不知道如何计算不同的形状(心,闪电,盾牌,数字)。

如果有人给我提示或解决方法,我会很高兴。

1)对不起,英语不好 2)我们在类中没有openCV [只有基本的c ++] 3)试图在互联网上找到有用的东西,但是即使我发现了什么,我也无法理解代码中发生了什么

1 个答案:

答案 0 :(得分:0)

您的项目可以分为三个步骤:

  1. 找到骰子。
  2. 从可见表面提取形状 骰子。
  3. 数脸。

对于所有可能的方法中的第一步,我认为显着性图方法可以提供帮助。 显着图是一种分割算法,旨在检测图像中更可能引起视觉关注的部分。

OpenCV具有一个显着性API,该API已经实现了几种显着性算法,对于每个显着性算法,您都可以获取一个分割图。

很可能考虑您为显着性提供的示例图像将重点放在骰子上。

因此,您可以从原始图像中提取骰子作为rois。

对于步骤2),显着性算法也可能适合...或者不适合...取决于算法使用的许多统计标准。 但是,先前提取的rois应该只包含确实包含您要在步骤3)中计数的形状的骰子的面,因此基于轮廓检测的方法可能会得出很好的结果。

一旦获得了用于计算每个形状的形状,就可以使用templateMatching(在OpenCV中也已实现),基于形状敏感度量(Hausdorff,Dice等)的聚类方法,或者还有很多。

这是可以帮助您完成第一步的两个代码。

#ifndef _DEBUG
#define _DEBUG
#endif

#include <iostream>

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

#include <list>

CV_EXPORTS_W void get_regions_of_interest(cv::InputArray _src, cv::OutputArrayOfArrays mv, cv::OutputArrayOfArrays mv2 = cv::noArray());

int main()
{
    cv::Mat tmp = cv::imread("C:\Desktop\dices.jpg");

    if(!tmp.empty())
    {
        cv::imshow("source",tmp);
        cv::waitKey(-1);
    }

    std::vector<cv::Mat> rois;

    get_regions_of_interest(tmp,rois);

    std::cout << "Hello World!" << std::endl;
    return 0;
}

void get_regions_of_interest(cv::InputArray _src, cv::OutputArrayOfArrays _rois, cv::OutputArrayOfArrays _contours)
{

    // Check that the first argument is an image and the second a vector of images.
    CV_Assert(_src.isMat() && !_src.depth() && (_src.channels() == 1 || _src.channels() == 3) && _rois.isMatVector() && (!_contours.needed() || (_contours.needed() && _contours.isMatVector()) ) );

    static cv::Ptr<cv::saliency::StaticSaliencySpectralResidual> saliency;

    if(!saliency)
        saliency = cv::saliency::StaticSaliencySpectralResidual::create();

    cv::Mat src = _src.getMat();
    cv::Mat gray;

    if(src.depth() == src.type())
        gray = src;
    else
        cv::cvtColor(src,gray,cv::COLOR_BGR2GRAY);

    bool is_ctr_needed = _contours.needed();
    std::list<cv::Mat> final_ctrs;

    // Step 1) Process the saliency in order to segment the dices.

    cv::Mat saliency_map;
    cv::Mat binary_map;

    saliency->computeSaliency(src,saliency_map);
    saliency->computeBinaryMap(saliency_map,binary_map);


    saliency_map.release();

    // Step 2) From the binary map get the regions of interest.

    cv::Mat1i stats;
    std::vector<cv::Mat> rois;

    cv::Mat labels;
    cv::Mat centroids;

    cv::connectedComponentsWithStats(binary_map, labels, stats, centroids);


    labels.release();
    centroids.release();

    // prepare the memory
    rois.reserve(stats.rows-1);

// Sort the stats in order to remove the background.

    stats = stats.colRange(0,stats.cols-1);

    // Extract the rois.

    for(int i=0;i<stats.rows;i++)
    {
        cv::Rect roi = *reinterpret_cast<cv::Rect*>(stats.ptr<int>(i));

        if(static_cast<std::size_t>(roi.area()) == gray.total())
            continue;

        rois.push_back(gray(roi));
#ifdef _DEBUG
        cv::imshow("roi_"+std::to_string(i),gray(roi));
#endif
    }



    // Step 3) Refine.

    // Because the final number of shape cannot be determine in advance it is better to use a linked list than a vector.
    // In practice except if there is a huge number of elements to work with the performance will be almost the same.
    std::list<cv::Mat> shapes;

    int cnt=0;
    for(const cv::Mat& roi : rois)
    {

        cv::Mat tmp = roi.clone();

        // Slightly sharpen the regions contours
        cv::morphologyEx(tmp,tmp, cv::MORPH_CLOSE, cv::noArray());
        // Reduce the influence of local unhomogeneous illumination.
        cv::GaussianBlur(tmp,tmp,cv::Size(31,31), 5);

        cv::Mat thresh;
        // Binarize the image.
        cv::threshold(roi,thresh,0.,255.,cv::THRESH_BINARY | cv::THRESH_OTSU);
#ifdef _DEBUG
        cv::imshow("thresh"+std::to_string(cnt++),thresh);
#endif
        // Find the contours of each sub region on interest
        std::vector<cv::Mat> contours;

        cv::findContours(thresh, contours, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);

        cv::Mat dc;

        cv::merge(std::vector<cv::Mat>(3,thresh),dc);

//        cv::drawContours(dc, contours,-1,cv::Scalar(0.,0.,255),2);
//        cv::imshow("ctrs"+std::to_string(cnt),dc);

        // Extract the sub-regions

        if(is_ctr_needed)
        {
            for(const cv::Mat& ctrs: contours)
            {

                cv::Rect croi = cv::boundingRect(ctrs);

                // If the sub region is to big or to small it is depreate
                if(static_cast<std::size_t>(croi.area()) == roi.total() || croi.area()<50)
                    continue;

                final_ctrs.push_back(ctrs);

                shapes.push_back(roi(croi));

#ifdef _DEBUG
                cv::rectangle(dc,croi,cv::Scalar(0.,0.,255.));

                cv::imshow("sub_roi_"+std::to_string(cnt++),roi(croi));
#endif
            }
        }
        else
        {
            for(const cv::Mat& ctrs: contours)
            {

                cv::Rect croi = cv::boundingRect(ctrs);

                // If the sub region is to big or to small it is depreate
                if(static_cast<std::size_t>(croi.area()) == roi.total() || croi.area()<50)
                    continue;

                shapes.push_back(roi(croi));

#ifdef _DEBUG
                cv::rectangle(dc,croi,cv::Scalar(0.,0.,255.));

                cv::imshow("sub_roi_"+std::to_string(cnt++),roi(croi));
#endif

            }
        }

    }
#ifdef _DEBUG
    cv::waitKey(-1);
#endif

    // Final Step: set the output

    _rois.create(shapes.size(),1,CV_8U);
    _rois.assign(std::vector<cv::Mat>(shapes.begin(),shapes.end()));

    if(is_ctr_needed)
    {
        _contours.create(final_ctrs.size(),1,CV_32SC2);
        _contours.assign(std::vector<cv::Mat>(final_ctrs.begin(), final_ctrs.end()));
    }

}