Calculate percentage of certain colors in an image

I have a program that reads an image and checks if there are certain colors in that image, if yes it paints each pixel of a certain color. I could not find materials on the internet that can help me, only this page that has a solution using histograms, but I did not understand how this is done even with the code available. Is it really possible to use histograms? Is there any other easier way to calculate the percentage of colors of the image? I tried to use a rule of three to be able to calculate but returns absurd values.

Code snippet:

img = cv2.imread("IF23-1-2018.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, imgThresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
somenteAsfalto = cv2.inRange(img, asfaltoMax, asfaltoMin)
somenteTerra = cv2.inRange(img, terraMax, terraMin)
somenteVerde = cv2.inRange(img, verdeMax, verdeMin)

for i in range(0, altura):
    for j in range(0, largura):
        if somenteAsfalto[i,j] == imgThresh[i,j]:
            #pinta de vermelho onde tem asfalto
            img[i,j] = vermelho
            contAsfalto += 1
        if somenteTerra[i,j] == imgThresh[i,j]:
            #pinta de amarelo onde tem terra
            img[i,j] = amarelo
            contTerra += 1
        if somenteVerde[i,j] == imgThresh[i,j]:
            #pinta de violeta onde tem verde
            img[i,j] = violeta
            contVerde += 1
        if imgThresh[i,j] == 1 and imgThresh[i,j] == 0:
            img[i,j] = verde
            cont += 1
        if imgThresh[i,j] != somenteTerra[i,j]:
            img[i,j] = verdeCons

contNatural = contVerde + contTerra
contConstruido = contAsfalto + construido
porcVerde = (100*contVerde)/qntPixels
porcConst = (100*construido)/qntPixels
print("Pixels de asfalto: {}\nPixels de Terra: {}\nPixels onde tem verde: {}".format(contAsfalto, contTerra, contVerde))
print("Tamanho da imagem: {}".format(qntPixels))
print("Contador construido: {}\nContador contVerde: {}".format(construido, contVerde))
print("Porcentagem construĂ­da: {}%\nPorcentagem verde: {}%".format(porcConst, porcVerde))
print("DEU RUIM nÂș {}".format(cont))

Input image

Input image

Output image

Output image

Author: Marcos Paulo S. Rezende, 2019-01-07

1 answers

Count Pixels

Since the function inRange() is used, pixels can be counted with the function countNonZero().

Then the amount of pixels obtained in each inRange can be obtained as follows:

pixelsAsfalto = cv2.countNonZero(somenteAsfalto)
pixelsTerra = cv2.countNonZero(somenteTerra)
pixelsVerde = cv2.countNonZero(somenteVerde)

And the total amount of pixels in the image:

pixelsTotal = img.size

By dividing each amount of pixels by the total amount, you get the percentage.

Code

Then the code would look like this:

import cv2
import numpy as np
import matplotlib.pyplot as plt

def mostrar_inRange(img, mask):
    imask = mask > 0
    sliced = np.zeros_like(img, np.uint8)
    sliced[imask] = img[imask]
    plt.subplot(211)
    plt.imshow(sliced)
    plt.subplot(212)
    plt.imshow(img)
    plt.show()

img = cv2.imread("IF23-1-2018.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, imgThresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

somenteAsfalto = cv2.inRange(img, asfaltoMax, asfaltoMin)
somenteTerra = cv2.inRange(img, terraMax, terraMin)
somenteVerde = cv2.inRange(img, verdeMax, verdeMin)

pixelsAsfalto = cv2.countNonZero(somenteAsfalto)
pixelsTerra = cv2.countNonZero(somenteTerra)
pixelsVerde = cv2.countNonZero(somenteVerde)

pixelsTotal = img.size

mostrar_inRange(img, somenteAsfalto)
mostrar_inRange(img, somenteTerra)
mostrar_inRange(img, somenteVerde)

print('% Asfalto: ', (pixelsAsfalto/pixelsTotal)*100)
print('% Terra: ', (pixelsTerra /pixelsTotal)*100)
print('% Verde: ', (pixelsVerde/pixelsTotal)*100)

Note: A grayscale color segmentation is not the best option, as the image is colored the best would be to perform this in the HSV color space. But the picture quality is also not the best, and the color of the asphalt is very similar to the roof of the houses... Then the segmentation must be refined in other ways besides the use of color segmentation. An alternative to finding asphalt would be to limit the search range with the use of Hough Lines .

 2
Author: danieltakeshi, 2020-06-11 14:45:34