Image editing using Opencv with no ready-made functions

Asked

Viewed 3,056 times

3

I have the following picture shown below and I need to turn it into gray and then binarize it.

inserir a descrição da imagem aqui

I use the following code to show it on the screen

cv::Mat img = cv::imread("lena.jpg");// Lê a imagem no local onde ela é gerada(matriz)
cv::namedWindow("RGB");
cv::imshow("RGB",img); //abre a imagem na janela do windows de nome olá mundo
int keyCode = cvWaitKey( ); // Manter janela ativa equanto nenhuma tecla for pressionada

return 1;

But I need to turn this image into gray without using ready-made functions like cvCvtColor or cvThreshold, someone could help me by indicating at least one article that deals with the subject ?

1 answer

7

To do what you need, just scroll through the image pixels using the function Mat::at. This function allows you to access the value of a pixel in a given coordinate x and y.

Conversion to Grey

The conversion to grayscale can be performed in a few different ways. The simplest way is with the average method: you basically take the average of the values in each color band (red, green and blue) to get a single luminous intensity value. Another slightly better method is brightness extraction. In this method, you take the average only of the highest and lowest value of these bands (thus ignoring that intermediate color). Finally, another method is directly related to how humans perceive the world. The proportions employed in this formula derive from the proportions of the cones (photosensitive cells in the human retina):

inserir a descrição da imagem aqui

The average method is virtually the same as the brightness method, and they tend to reduce contrast. The luminance method works better overall, so much so that it is the method most used (including in editing tools such as Gimp or Photoshop). Another reason for it to be used is that the human being is more sensitive to green, and so this form of conversion usually generates images more pleasing to human perception.

Binary Image

On the limiarization (thresholding, the process used to create a binary image), this is an equally simple process. Simply process all pixels and exchange them to 0 (the equivalent in black) or 255 (the white equivalent) depending on whether they are above or below a chosen threshold. The choice of how to do it is a convention, but it often turns into 0 if the value is less than or equal and turn into 255 if it is greater than the threshold. The choice of threshold is based on what you want to do (for example, separate objects from the background) and also on the distribution of colors in the image (use the color histogram to make that analysis).

Examples

Here is an example code that does what has been explained:

#include <opencv2/highgui/highgui.hpp>

using namespace cv;

// Método da Média: Média dos valores das cores
uchar metodoMedia(Vec3b RGB)
{
    uchar azul = RGB.val[0];
    uchar verde = RGB.val[1];
    uchar vermelho = RGB.val[2];

    return (uchar) ((azul + verde + vermelho) / 3);
}

// Método do Brilho: Média dos valores máximos e mínimos das cores
uchar metodoBrilho(Vec3b RGB)
{
    uchar azul = RGB.val[0];
    uchar verde = RGB.val[1];
    uchar vermelho = RGB.val[2];

    return (uchar) ((std::max(std::max(azul, verde), vermelho) + std::min(std::min(azul, verde), vermelho)) / 2);
}

// Método da Luminosidade: Ponderação decorrente das proporções médias de cones no olho humano
uchar metodoLuminosidade(Vec3b RGB)
{
    uchar azul = RGB.val[0];
    uchar verde = RGB.val[1];
    uchar vermelho = RGB.val[2];

    return (uchar) ((0.21 * vermelho) + (0.72 * verde) + (0.07 * azul));
}

int main()
{
    // --------------------------------------------
    // Carrega a imagem original
    // --------------------------------------------

    Mat img = imread("lena.jpg"); // A imagem RGB carregada é CV_8UC3 porque tem três canais, as cores (R + G + B).

    int largura = img.size().width;
    int altura = img.size().height;

    // --------------------------------------------
    // Cria uma nova imagem em tons de cinza
    // (com o método de luminosidade)
    // --------------------------------------------

    Mat gray(largura, altura, CV_8UC1); // A nova imagem criada só tem 1 canal (CV_8UC1), a intensidade luminosa.

    int x, y;
    for(x = 0; x < largura; x++)
    {
        for(y = 0; y < altura; y++)
        {
            Vec3b pixel = img.at<Vec3b>(x, y);
            uchar intensidade = metodoLuminosidade(pixel);
            gray.at<uchar>(x, y) = intensidade;
        }
    }

    // --------------------------------------------
    // Cria uma imagem binarizada
    // --------------------------------------------

    Mat bin(largura, altura, CV_8UC1); // Essa imagem também só tem 1 canal, de preto e branco.
    uchar limiar = 128; // Limiar utilizado.

    // O método é o seguinte:
    // Pixels com luminosidade abaixo do limiar se tornam "preto" (0), e acima se tornam "branco" (255).
    for(x = 0; x < largura; x++)
    {
        for(y = 0; y < altura; y++)
        {
            Vec3b pixel = img.at<Vec3b>(x, y);
            uchar intensidade = metodoLuminosidade(pixel);

            if(intensidade <= limiar)
                bin.at<uchar>(x, y) = 0;
            else
                bin.at<uchar>(x, y) = 255;
        }
    }

    namedWindow("Imagem Original");
    imshow("Imagem Original",img);

    namedWindow("Imagem em Cinza");
    imshow("Imagem em Cinza", gray);

    namedWindow("Imagem Binária");
    imshow("Imagem Binária", bin);

    cvWaitKey();

    return 0;
}

The result of this code are the following windows:

inserir a descrição da imagem aqui

Although the method of luminosity (that resulting from human perception) is the most used, it is noted that the other methods generate quite similar results. Look at the examples below (I did not put the code that generates these images, but just use the three methods that exist in the previous code):

inserir a descrição da imagem aqui

It should be possible to notice that the last image is darker (has a higher average luminous intensity) and also more beautiful (although this is a matter of opinion). This is due to the fact that more weight is given to green in the method of luminosity, as occurs in human perception. Still, there may be some use where the other methods are interesting (when you want to reduce the contrast between light and dark in the image).

IMPORTANT: Note in the code (mainly in the conversion functions for the various methods) that although I (and you) call the pattern three bands of RGB (because of Red, Green and Blue), Opencv uses BGR in the manipulation of your images!

Browser other questions tagged

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