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
Output image
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 .