【问题标题】:Comic Balloon Detection: How can I count white pixels inside a vector<RotatedRect> Ellipse in OpenCV?漫画气球检测:如何计算 OpenCV 中矢量<RotatedRect> Ellipse 内的白色像素?
【发布时间】:2015-05-22 02:53:50
【问题描述】:

我一直在寻找答案,但我找不到答案。

我正在制作一个漫画气球检测程序,我需要找到一个在轮廓内具有特定百分比白色的椭圆(百分比稍后确定),因此我需要计算轮廓内的白色像素我不知道怎么做。

我已经尝试过countNonZero(),但由于它的参数是一个数组,它不接受我声明为vector&lt;RotatedRect&gt;minEllipse[i]contours[i]

下面是代码:

// Modified version of thresold_callback function 
// from http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/bounding_rotated_ellipses/bounding_rotated_ellipses.html
            Mat fittingEllipse(int, void*, Mat inputImage)
            {
                Mat threshold_output;
                vector<vector<Point> > contours;
                vector<Vec4i> hierarchy;
                int numberOfCaptions = 0;

                // Detect edges using Threshold
                threshold(inputImage, threshold_output, 224, 250, THRESH_BINARY);

                findContours(inputImage, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

                vector<RotatedRect> minEllipse(contours.size());
                Mat drawing = Mat::zeros(inputImage.size(), CV_8UC3);

                for (int i = 0; i < contours.size(); i++)
                {
                    if (contours[i].size() > 5)
                        minEllipse[i] = fitEllipse(Mat(contours[i]));
                }

                int totalContourSize = 0, whitepixels, blackpixels;

                //Draw ellipse/caption
                for (int i = 0; i < contours.size(); i++)
                {
                    Scalar color = Scalar(255, 0, 0);

                    if (minEllipse[i].size.height >= inputImage.rows / 8 && //IJIP-290-libre.pdf
                        minEllipse[i].size.width >= inputImage.cols / 10 && //IJIP-290-libre.pdf
                        minEllipse[i].size.height < inputImage.rows / 3  &&
                        minEllipse[i].size.width < inputImage.cols / 3 &&
                        (
                        (minEllipse[i].angle >= 0 && minEllipse[i].angle <= 10) ||
                        (minEllipse[i].angle >= 80 && minEllipse[i].angle <= 100) ||
                        (minEllipse[i].angle >= 170 && minEllipse[i].angle <= 190) ||
                        (minEllipse[i].angle >= 260 && minEllipse[i].angle <= 280) ||
                        (minEllipse[i].angle >= 350 && minEllipse[i].angle <= 360)
                        )) {

                        ellipse(drawing, minEllipse[i], color, -1, 8);
                    }
                }

                drawing = binarizeImage(drawing);
                return drawing;
            } // end of fittingEllipse


            Mat CaptionDetection(Mat inputImage){
                Mat outputImage, binaryImage, captionDetectImage;

                binaryImage = captionDetectImage = binarizeImage(inputImage);
                threshold(captionDetectImage, captionDetectImage, 224, 250, 0); //IJIP-290-libre.pdf

                GaussianBlur(captionDetectImage, captionDetectImage, Size(9, 9), 0, 0);
                captionDetectImage = fittingEllipse(0, 0, captionDetectImage);

                //binaryImage = invertImage(binaryImage);

                outputImage = inputImage;

                for (int i = 0; i < inputImage.rows; i++) {
                    for (int j = 0; j < inputImage.cols; j++) {
                        if (captionDetectImage.at<uchar>(i, j) == 0) {
                            outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[2] = 0;
                        }
                    }
                }

                return outputImage;
            } // end of CaptionDetection

非常庞大的 if 语句让我获得漫画气球检测的准确率只有 53%(更不用说所有的错误检测了),这就是为什么我需要获取轮廓中白色像素的百分比以得到更高的百分比。

编辑:

我想要的输出是整个漫画页面除了漫画气球之外都是黑色的,然后计算那里的白色和黑色像素的数量

CaptionDetection 函数上我应该计算每个字幕的像素数

最终答案

我编辑了用户 Kornel 提供的代码

            Mat fittingEllipse(int, void*, Mat inputImage)
            {
                Mat outputImage;
                vector<Vec4i> hierarchy;
                int numberOfCaptions = 0;

                // Detect edges using Threshold
                threshold(inputImage, inputImage, 224, 250, THRESH_BINARY);

                findContours(inputImage, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

                vector<RotatedRect> minEllipse(contours.size());

                for (int i = 0; i < contours.size(); i++)
                {
                    if (contours[i].size() > 5)
                        minEllipse[i] = fitEllipse(Mat(contours[i]));
                }

                //Draw ellipse/caption
                outputImage = Mat::zeros(inputImage.size(), CV_8UC3);
                for (int i = 0; i < contours.size(); i++)
                {
                    Scalar color = Scalar(255, 255, 255);
                    Mat drawing = Mat::zeros(inputImage.size(), CV_8UC3);

                    ellipse(drawing, minEllipse[i], color, -1, 8);

                    drawing = binarizeImage(drawing);
                    int area = countNonZero(drawing);

                    if ((area >= 10000 && area <= 40000) &&
                        (
                        (minEllipse[i].angle >= 0 && minEllipse[i].angle <= 10) ||
                        (minEllipse[i].angle >= 80 && minEllipse[i].angle <= 100) ||
                        (minEllipse[i].angle >= 170 && minEllipse[i].angle <= 190) ||
                        (minEllipse[i].angle >= 260 && minEllipse[i].angle <= 280) ||
                        (minEllipse[i].angle >= 350 && minEllipse[i].angle <= 360)
                        )){
                        ellipse(outputImage, minEllipse[i], color, -1, 8);
                        captionMask[captionCount] = drawing;
                        captionCount++;
                    }
                }

                imwrite((string)SAVE_FILE_DEST + "out.jpg", outputImage);

                return outputImage;
            } // end of fittingEllipse
            Mat replaceROIWithOrigImage(Mat inputImg, Mat mask, int k){
                Mat outputImage = inputImg;
                Mat maskImg = mask;
                imwrite((string)SAVE_FILE_DEST + "inputbefore[" + to_string(k) + "].jpg", inputImg);
                for (int i = 0; i < inputImg.rows; i++) {
                    for (int j = 0; j < inputImg.cols; j++) {

                        if (maskImg.at<uchar>(i, j) == 0) {
                            inputImg.at<Vec3b>(i, j)[0] = inputImg.at<Vec3b>(i, j)[1] = inputImg.at<Vec3b>(i, j)[2] = 0;
                        }

                    }
                }
                imwrite((string)SAVE_FILE_DEST + "maskafter[" + to_string(k) + "].jpg", inputImg);
                return inputImg;
            }

            Mat CaptionDetection(Mat inputImage){
                Mat outputImage, binaryImage, captionDetectImage;

                binaryImage = captionDetectImage = binarizeImage(inputImage);
                threshold(captionDetectImage, captionDetectImage, 224, 250, 0); //IJIP-290-libre.pdf

                GaussianBlur(captionDetectImage, captionDetectImage, Size(9, 9), 0, 0);
                captionDetectImage = fittingEllipse(0, 0, captionDetectImage);

                for (int i = 0; i < captionCount; i++){

                    Mat replacedImg = replaceROIWithOrigImage(inputImage.clone(), captionMask[i], i);

                    int area = countNonZero(binarizeImage(replacedImg));

                    cout << area << endl;
                }

                return outputImage;
            } // end of CaptionDetection

fittingEllipse() 中的 if 条件将在以后进行编辑以提高准确性。

感谢您的帮助和时间用户 a-Jays 和 Kornel!

【问题讨论】:

  • 一旦你有了一个轮廓,找到你的非零像素inside那个轮廓。
  • @a-Jays 抱歉,这是我的问题,我不知道如何在轮廓内找到它。
  • 在图像上使用轮廓形状的蒙版,然后在新蒙版图像中找到非零像素。 stackoverflow.com/questions/8145036/…
  • @a-Jays 但这样做我只会得到一个普通的白色椭圆,我需要检测中间有字符的椭圆然后计算白色像素
  • 如果您可以发布图片,那将会很有帮助。否则,我不明白为什么中间有文字会影响最外面的轮廓。

标签: c++ opencv image-processing feature-detection feature-extraction


【解决方案1】:

假设您有一个旋转矩形 rRect,它定义了一个椭圆,就像您的代码中的 minEllipse[i] 一样。

首先,它的面积可以通过封闭公式area = a * b * PI来估计,其中ab是长半轴和短半轴(椭圆长短轴的1⁄2)轴),所以:

cv::RotatedRect rRect(cv::Point2f(100.0f, 100.0f), cv::Size2f(100.0f, 50.0f), 30.0f);
float area = (rRect.size.width / 2.0f) * (rRect.size.height / 2.0f) * M_PI;

或者更短一点:

float area = (rRect.size.area() / 4.0f) * M_PI;

或者,您可以简单地将其绘制在 cv::ellipse() 的蒙版上,即:

cv::Mat mask = cv::Mat::zeros(200, 200, CV_8UC1);
cv::ellipse(mask, rRect, cv::Scalar::all(255), -1);

你照常计算非零元素:

int area = cv::countNonZero(mask);

【讨论】:

  • 感谢您的回复,但是如何计算轮廓中的白色像素?
  • 通过轮廓,我的意思是轮廓内的图像中的漫画(我说得通吗?)我没有足够的声誉来发布图片,对不起:'(
  • 你能画出最外层的轮廓吗?您应该至少为您的图片添加一个外部链接以澄清问题。
  • 我设法编辑了您提供的代码,以计算所有白色像素以进行字幕检测,非常感谢 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-03
  • 2014-05-22
  • 1970-01-01
  • 2012-04-27
  • 2012-01-15
  • 2014-09-30
  • 1970-01-01
相关资源
最近更新 更多