I'm trying to build a program to count the number of pipes from a given image,https://drive.google.com/drive/folders/1iw2W7dUg3ICGRt3hxOynUJCf-KQj2Pka?usp=sharing are some example test images.

I've tried doing the same with Canny's, Hough's but neither of them seem to be even close to counting them properly. What approach should I go for?

2

Best Answer


There are several things to consider:

  1. It is better to find the range of pipes with a suitable method (search the net).
  2. In the second step, with PerspectiveTransform, preferably eliminate the rotation of the image and only move the range of the pipes to the next step.
  3. In the next step, from now on, you can use the following algorithm.

This is not a complete algorithm and will not work for all test_cases. You have to spend time. Read and combine different image processing methods or maybe even machine learning; Change the parameters to get a better result.

Another point is to try to keep the environmental conditions constant.

import sysimport cv2import numpy as np# Load imageim = cv2.imread(sys.path[0]+'/im.jpeg')H, W = im.shape[:2]# Make a copy from imageout = im.copy()# Make a grayscale versiongry = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)# Make a clean black and white version of that picturebw = cv2.adaptiveThreshold(gry, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 12)bw = cv2.medianBlur(bw, 3)bw = cv2.erode(bw, np.ones((5, 5)))bw = cv2.medianBlur(bw, 9)bw = cv2.dilate(bw, np.ones((5, 5)))# Draw a rectangle around image to eliminate errorscv2.rectangle(bw, (0, 0), (W, H), 0, thickness=17)# Count number of pipescnts, _ = cv2.findContours(bw, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)# Change channels of black/white imagebw = cv2.cvtColor(bw, cv2.COLOR_GRAY2BGR)# Draw position of pipesc = 0for cnt in cnts:c += 1x, y, w, h = cv2.boundingRect(cnt)cv2.circle(out, (x+w//2, y+h//2), max(w, h)//2, (c, 220, 255-c), 2)if c >= 255:c = 0# Count and print number of pipesprint(len(cnts))# Save output imagescv2.imwrite(sys.path[0]+'/im_out.jpg', np.hstack((bw, out)))

enter image description here

Although you may get by with some ad-hoc algorithm, I think you would spend more time tuning it than just training and tuning a model:

The pipes do not seem to have a very complicated texture, so I would create a set of synthetic images by cropping some of the pipes from the images and then I would apply multiple transformations to it. For instance, you can use the imgaug (https://imgaug.readthedocs.io/en/latest/) library in python. Then, you can use HOG, Haar features, LBP, etc.. to get features and train a model adaboost with trees, linear SVM, etc... The object detection pipeline from DLib should be good enough and is easy to use. In general, you need a framework with sliding windows for the detection part.

Another option is to get a bunch of backgrounds as well as transformations over the cropping images and create multiple images with bounding boxes annotations, you could use a tool like this one for that: https://github.com/tylerhutcherson/synthetic-images.

Then, you can use a deep learning model like YOLOv4, RetinaNet, or Faster-RCNN. Notice that you may need to change the strides of the output layers since the objects may be too small.

All in all, If I were you I would start with the object detection from dlib, it should not take you too long to have everything ready.

One thing, given the type of features used for the training the model may detect circles that are not really pipes, but if you expect to have many pipes, that should be easy to remove.

Hope it helps and good luck.