Home > Software design >  Counting pixels in all distinct regions in an image using OpenCV
Counting pixels in all distinct regions in an image using OpenCV

Time:12-10

I have an image that looks like this (it's blown up, but each square is just one pixel). I'd like to be able to count the number of pixels in every black region. Region is defined as horizontally and vertically adjacent pixels (not diagonally). So in this image there are four of them. The output could be either some kind of dictionary with region identifiers (like centroids maybe) mapped to the number of pixels in that region, or just a list of totals would be fine like [14, 3, 9, 9].

I can't seem to figure out how to get the actual number of pixels to work. I got this far in my Googling of how the connectedComponentsWithStats function works. I tried blurring the image and everything to get rid of the possible "diagonal" connections. I'm assuming it's the one I'm supposed to use, but maybe you guys know of something better?

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import cv2

d = [[9, 8, 9, 3, 2], [8, 7, 8, 9, 1], [9, 6, 5, 8, 9], [9, 7, 6, 7, 9], [9, 8, 7, 8, 9], [6, 9, 8, 9, 4], [5, 6, 9, 4, 3], [6, 7, 8, 9, 2], [7, 8, 9, 2, 1], [8, 9, 2, 1, 0]]
a = np.array([[0 if x < 9 else 255 for x in z] for z in d])

img = Image.fromarray(a.astype(np.uint8), mode='L')
img.save("test.jpg")

img = cv2.imread('test.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (1, 1), 0)

components, output, stats, centroids = cv2.connectedComponentsWithStats(blurred, connectivity = 4)
output

CodePudding user response:

If you want to count pixels, OpenCV has a function for that: cv2.countNonZero. This counts white pixels, but you can easily count black pixels by first computing the image height and width.

Your code would be modified as follows:

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import cv2


d = [[9, 8, 9, 3, 2], [8, 7, 8, 9, 1], [9, 6, 5, 8, 9], [9, 7, 6, 7, 9], [9, 8, 7, 8, 9], [6, 9, 8, 9, 4],
     [5, 6, 9, 4, 3], [6, 7, 8, 9, 2], [7, 8, 9, 2, 1], [8, 9, 2, 1, 0]]
a = np.array([[0 if x < 9 else 255 for x in z] for z in d])

img = Image.fromarray(a.astype(np.uint8), mode='L')
img.save("test.jpg")

img = cv2.imread('test.jpg')

# Display image:
cv2.imshow("Image", img)
cv2.waitKey(0)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (1, 1), 0)

# Count number of white pixels:
whitePixels = cv2.countNonZero(blurred)

print("White pixels: " str(whitePixels))

# Get image height and width:
(imgHeight, imgWidth) = blurred.shape[:2]

# Get number of black pixels:
blackPixels = imgHeight*imgWidth - whitePixels

print("Black pixels: " str(blackPixels))

Which prints:

White pixels: 34
Black pixels: 16

Pro-tip: Do not use the jpg image format, it is lossy - it will compress and modify the value of your pixels. Use a lossless format such as png.

CodePudding user response:

connectedComponentsWithStats already gives you area, which is the count of pixels in each component (of non-zero pixels) that can be found in the picture.

Pay attention to Beaker's comment: CC has a connectivity parameter, which you should set to "4-way".

You should invert your picture, so the black pixels become white. white is non-zero is true and that's always foreground, which is what the CC call expects to work with.

Documentation: https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gac7099124c0390051c6970a987e7dc5c5

The gaussian blur will turn any binarized image into a soup of grayscale values. Don't apply the gaussian blur, or if you require it, then threshold afterwards. connectedComponents* will threat any non-zero (!) pixel as foreground

  • Related