0

I have a sample image:

Source image

and I use different thresholding methods in order to count the number of pixels.

First Method is simple thresholding since on the source image I only have a one colored object against a white background.

    Mat image = imread("/$image_path", IMREAD_GRAYSCALE);
    Mat binary_image;
    threshold(image, binary_image, 120, 255, THRESH_BINARY);
    int TotalNumberOfPixels = binary_image.rows * binary_image.cols;
    int PixelCount = TotalNumberOfPixels - cv::countNonZero(binary_image);
    return PixelCount;

The second method is assuming I have an image with multiple colored objects (ie multiple colored marks) hence I need to filter and apply a red mask. I did it via:

Mat image2 = imread("/$image_path", IMREAD_COLOR);
Mat blurred, edge;
Mat bgrInv = ~image2;
Mat hsvIm;
Mat maskRed;    

cvtColor(bgrInv, hsvIm, COLOR_BGR2HSV);

inRange(hsvIm, Scalar(80, 70, 239), Scalar(100, 255, 255), maskRed);
imshow("Mask", maskRed);

//blur(maskRed, blurred, Size(3, 3));
//Canny(blurred, edge, 75, 200, 3);

cout << "Pixel Count: " << countNonZero(maskRed)<< endl;

The output for both methods are:

Method 1: 406
Method 2: 155

I will be operating on a colored image hence I was using the second method at first. But I do not know if it will be "accurate" or correct.

Here is the sample template that I am working on. Its basically a survey type template with minor colored blocks. With red circles as mark placeholders for easier pre/post processing.

Kinght 金
  • 17,681
  • 4
  • 60
  • 74
Fer-de-lance
  • 375
  • 5
  • 23

2 Answers2

0

Let's have a look at the outputs of your two methods.

This is binary_image from method #1:

enter image description here

You count - cumbersomely (is this proper English?) - the black pixels, which corresponds to your task to count the red pixels. (By the way, invert the threshold and just count the white pixels.)

This is edge from method #2:

enter image description here

You count the white pixels, but as you can see, this is only the outline of the initial red object. And this does NOT correspond to your original task.

So, given both methods as they are, the first is more "accurate", at the moment and for the given example. Nevertheless, you mentioned objects of different colors, so method #2 should be reworked to count the proper pixels.

Could you please give examples for images with multiple objects of different color?

Also, I edited your question. (The edit isn't reviewed, yet.) The image loading part is important, since I guess, in method #1 you used imread(..., IMREAD_GRAYSCALE) and imread(..., IMREAD_COLOR) in method #2.

HansHirse
  • 18,010
  • 10
  • 38
  • 67
  • Yes, I already added how I read the image in the code and I also included the template I'm working on. I fixed my code as I was counting pixels on `edge` instead of `maskRed`. I also adjusted my HSV values which resulted to a count of 366 for that method. – Fer-de-lance Feb 28 '19 at 06:23
  • I was counting the "black" pixels in the image for the first method as I will be using them later to check if the mark is valid or not. – Fer-de-lance Feb 28 '19 at 06:46
0

I think all methods are not accurate if your image is blured (such as in JPEG format). But let's assume it's clear.

To count colored object pixels, we can count all colored pixels or count all RED pixels.

(1) Find the colored regions in HSV: How to detect colored patches in an image using OpenCV?

# count colored pixels in S(HSV)
def countColored(img):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h,s,v = cv2.split(hsv)
    num = np.sum(s>20)
    return num

(2) Find the Red regions: How to find the RED color regions using OpenCV?

def countRed(img):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h,s,v = cv2.split(hsv)
    mask1 = cv2.inRange(hsv, (0,50,20), (5,255,255))
    mask2 = cv2.inRange(hsv, (175,50,20), (180,255,255))
    mask = cv2.bitwise_or(mask1, mask2 )
    num = cv2.countNonZero(mask)
    return num

#!/usr/bin/python3
# 2019/02/28
import cv2
import numpy as np

def cvshow(img):
    cv2.imshow("OpenCV", img)
    cv2.waitKey();cv2.destroyAllWindows()

def countColored(img):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h,s,v = cv2.split(hsv)
    num = np.sum(s>20)
    return num

def countRed(img):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h,s,v = cv2.split(hsv)
    mask1 = cv2.inRange(hsv, (0,50,20), (5,255,255))
    mask2 = cv2.inRange(hsv, (175,50,20), (180,255,255))
    mask = cv2.bitwise_or(mask1, mask2 )
    num = cv2.countNonZero(mask)
    return num

if __name__ == "__main__":
    fpath = "fQipc.jpg"
    img = cv2.imread(fpath)
    num1 = countColored(img)
    num2 = countRed(img)
    print(num1, num2)
    # 643, 555
Kinght 金
  • 17,681
  • 4
  • 60
  • 74
  • Since I'm still new here, I wanted to know if it's appropriate - especially in the context of OpenCV - to answer questions using another language than originally used (or tagged) by the question author. – HansHirse Feb 28 '19 at 06:28
  • I may do dual masking for other projects but for now I'll stick using a single mask. The values I got for my HSV came from my own template of selecting HSV values for an image. I am happy that I can isolate what I need for now. For the image format should I use png? – Fer-de-lance Feb 28 '19 at 06:45
  • @Fer-de-lance Yes, I think you should use `png` format to get more accurate result. – Kinght 金 Feb 28 '19 at 07:10
  • @HansHirse That is a slightly troubled question and there are endless disputes about it on StackOverflow Meta. Personally I think it's generally ok as question-asker may not be aware of the technique/technology you want to suggest, and if he doesn't like your answer he can ignore it and it will naturally sink to the bottom of the page, but if he does like it, he may be delighted you suggested it. Be prepared to be down-voted for doing it sometimes, and use your discretion, i.e. don't recommend paid for software you develop yourself. – Mark Setchell Feb 28 '19 at 07:31