Calculation of figures area

Asked

Viewed 551 times

0

I may have something on the subject here, but I haven’t found it. I am new in the area and would like help to elaborate a C++ algorithm for detecting the area of a predetermined color in a figure. That is: the user inserts only the image in the program, and the program returns to it the area of the red color, for example (the user does not choose the color, it is already embedded in the program, it will always be the same). I would very much like the suggestion of a tutorial (or more than one) to follow or someone else’s instruction. I’m using Visual Studio already with Opencv.

1 answer

2

Well, you did not provide important details of your problem (for example: What type of image is it? Is the red color pure red, or is it a range of shades of red?) and it is also unclear what you really want when you say "the program returns to it the area" (the program calculates the area in pixels? the program highlights the area with another color? etc). So I made a simple example that can help you start to understand the problem. It uses simple operations, in which you search and change the pixel values by comparing them to a desired color.

In the following code example, I look for the EXACT red color (in BGR code - the one used by Opencv - that means (0, 0, 255), because it is 0 for blue/blue, 0 for green/green and 255 for red/red), count the number of pixels that have this color and I already replace them with another color given (in the example, yellow, to highlight the change):

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <string>
#include <iomanip>
#include <iostream>

using namespace std;
using namespace cv;

/**
 * Conta o número de pixels na imagem com a primeira cor e os substitui pela segunda cor.
 * @param oImagem cv::Mat com os dados da imagem colorida.
 * @param oCor cv::Vec3b com os valores BGR dos pixels a serem contados/substituidos.
 * @param oNovaCor cv::Vec3b com os valores BGR para substituir os pixels encontrados.
 * @return Retorna um inteiro com o número de pixels encontrados com a cor dada.
 */
int processaCorExata(Mat &oImagem, Vec3b oCor, Vec3b oNovaCor)
{
    Vec3b oPixel;
    int iPixels = 0;

    for (int x = 0; x < oImagem.size().width; x++)
    {
        for (int y = 0; y < oImagem.size().height; y++)
        {
            oPixel = oImagem.at<Vec3b>(y, x);

            // Checa se o pixel tem EXATAMENTE a mesma cor
            if (oPixel[0] == oCor[0] && oPixel[1] == oCor[1] && oPixel[2] == oCor[2])
            {
                iPixels++; // Contabiliza o pixel
                oImagem.at<Vec3b>(y, x) = oNovaCor; // Substitui pela nova cor dada
            }
        }
    }
    return iPixels;
}

/**
 * Função principal.
 * @param argc Inteiro com o número de argumentos da linha de comando.
 * @param argv Lista de strings com os argumentos da linha de comando.
 * @return Retorna um inteiro com o código de erro ou 0 se encerrado com sucesso.
 */
int main(int argc, char** argv)
{
    // Carrega a imagem colorida de exemplo
    Mat oImagem = imread("C:/Temp/rgb.png", CV_LOAD_IMAGE_COLOR);
    if (!oImagem.data)
    {
        cout << "Erro carregando a imagem" << endl;
        return -1;
    }

    // Exibe a imagem original em uma janela
    namedWindow("Imagem Original", WINDOW_AUTOSIZE);
    imshow("Imagem Original", oImagem);

    // Definição das cores
    Vec3b oVermelho(0, 0, 255);
    Vec3b oAmarelo(0, 255, 255);

    // Conta os pixels em vermelho e os substitui por amarelo
    int iPixels = processaCorExata(oImagem, oVermelho, oAmarelo);

    // Calcula a área em vermelho (número de pixels e percentual sobre a área total da imagem)
    int iAreaTotal = oImagem.size().width * oImagem.size().height;
    ostringstream sMsgBuilder;
    sMsgBuilder << "Pixels vermelhos: " << iPixels;
    sMsgBuilder << " (" << std::fixed << std::setprecision(2) << (iPixels / (float)iAreaTotal) * 10.0 << "%)";
    string sMsg = sMsgBuilder.str();

    // Exibe essa informação na saída padrão e também na imagem
    cout << sMsg << endl;

    double dScale = 0.5;
    int iThickness = 3;
    int iFontFace = FONT_HERSHEY_SCRIPT_SIMPLEX;
    int iBaseline = 0;
    Size oSize = getTextSize(sMsg.c_str(), iFontFace, dScale, iThickness, &iBaseline);
    iBaseline += iThickness;

    Point oPos(iThickness, oImagem.rows - oSize.height - iThickness);
    putText(oImagem, sMsg.c_str(), oPos, FONT_HERSHEY_SCRIPT_SIMPLEX, 1.0, Scalar::all(0));

    // Exibe a imagem processada em uma nova janela
    namedWindow("Imagem Processada", WINDOW_AUTOSIZE);
    imshow("Imagem Processada", oImagem);

    // Aguarda o usuário fechar as janelas ou pressionar qualquer tecla
    waitKey(0);
    return 0;
}

The sample image used in my tests is this:

inserir a descrição da imagem aqui

That after processing generates the following result:

inserir a descrição da imagem aqui

IMPORTANT: Note, however, that this program will not work as expected for images with more distinct colors (i.e., other shades of red). There are alternatives, of course, for these cases. You can try to do liming (see function threshold from Opencv), or generate vc even masks with larger thresholds and directly use the function inRange Opencv (a more recommended method than doing the manipulations I exemplify in the above code, if the images of your problem domain are really more complex).

P.S.: The logic is the same as those of my other answers in C# and in Java.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.