Comic Balloon Detection: как подсчитать белые пиксели внутри вектора‹RotatedRect›Ellipse в OpenCV?

Я искал ответ везде, где мог, но не нашел.

Я делаю программу обнаружения комического воздушного шара, и мне нужно найти эллипс, который имеет определенный процент белого внутри контура (процент будет определен позже), поэтому мне нужно подсчитать белые пиксели внутри контура, и я не не знаю как.

Я пробовал countNonZero(), но, поскольку параметр этого массива, он не принимает мои minEllipse[i] или contours[i], которые объявлены как vector<RotatedRect>.

Ниже приведен код:

// 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

Условие if в fittingEllipse() должно быть позже отредактировано для большей точности.

Спасибо за помощь и время пользователям a-Jays и Kornel!


person R.A.    schedule 19.03.2015    source источник
comment
Получив контур, найдите ненулевые пиксели внутри этого контура.   -  person a-Jays    schedule 19.03.2015
comment
@a-Jays Извините, но это моя проблема, я не знаю, как найти это внутри контура.   -  person R.A.    schedule 20.03.2015
comment
Используйте маску формы вашего контура на вашем изображении, затем найдите ненулевые пиксели в новом замаскированном изображении. stackoverflow.com/questions/8145036/   -  person a-Jays    schedule 20.03.2015
comment
@a-Jays, но при этом я получу только один простой белый эллипс, мне нужно обнаружить эллипс с символами в середине, а затем подсчитать белые пиксели.   -  person R.A.    schedule 22.03.2015
comment
Если бы вы могли размещать изображения, это было бы полезно. В противном случае я не понимаю, почему наличие текста между ними повлияет на самый внешний контур.   -  person a-Jays    schedule 24.03.2015
comment
@a-Jays У меня недостаточно репутации, чтобы публиковать изображения, извините   -  person R.A.    schedule 24.03.2015
comment
Я, по общему признанию, все еще далек от понимания вашей проблемы, но есть одно предложение: вам не нужно перебирать все пиксели, чтобы применить маску. Вы можете использовать sourceImg.copyTo( destImage, maskImg ).   -  person a-Jays    schedule 26.03.2015


Ответы (1)


Скажем, у вас есть повернутый прямоугольник rRect, который определяет эллипс точно так же, как minEllipse[i] в вашем коде.

Во-первых, его площадь можно оценить по закрытой формуле area = a * b * PI, где a и b — большая и малая полуоси (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);
person Kornel    schedule 19.03.2015
comment
Спасибо за ответ, но как мне посчитать белые пиксели в контуре? - person R.A.; 20.03.2015
comment
Под контуром я подразумеваю мангу на изображении внутри контура (я имею смысл?) У меня недостаточно репутации, чтобы публиковать картинки, извините :'( - person R.A.; 20.03.2015
comment
Можете ли вы нарисовать самые внешние контуры? Вы должны добавить внешнюю ссылку на свои изображения хотя бы для того, чтобы прояснить проблему. - person Kornel; 23.03.2015
comment
Мне удалось отредактировать код, который вы дали, чтобы подсчитать все белые пиксели для обнаружения подписи, большое спасибо :) - person R.A.; 24.03.2015