0

I am currently working on a project to measure water levels in an open channel with image processing. My main issue is that I have to work on rather problematic images because of our lab conditions. ( i.e. bad lighting, bad background etc.) current sample image

Currently this is the best image I have, I will get better ones in the future but for now this is what I'm working with.

Since there's so much going on in the picture, I decided to filter blue color to draw some contours and pick the ones I need to work on. But I can't seem to get around it well enough to detect every blue rectangle. I've also tried to just grayscale the image and then draw contours, which seems to work better but still not enough. For measurement I'm planning on using Hough Transform.

My question is; am I approaching this completely wrong, or the image I'm currently working with is simply not good enough to work on? Should I try different libraries or languages? Here's my current code;

from imutils import perspective
from imutils import contours
import numpy as np
import imutils
import cv2 as cv


blueLow = np.array([90, 50, 20])
blueHigh = np.array([130, 255, 255])

img = cv.imread("10ltsn.png")
imgHSV = cv.cvtColor(img, cv.COLOR_BGR2HSV)
mask = cv.inRange(imgHSV, blueLow, blueHigh)
##imgray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

##kernelOpen = np.ones((5,5))
##kernelClose = np.ones((20,20))
##maskOpen = cv.morphologyEx(mask, cv.MORPH_OPEN, kernelOpen)
##maskClose = cv.morphologyEx(maskOpen, cv.MORPH_CLOSE, kernelClose)

##imgray = cv.GaussianBlur(imgray, (5,5), 0)
##imgray = cv.bilateralFilter(imgray,9,75,75)

edge = cv.Canny(mask, 50, 200)
edge = cv.dilate(edge, None, iterations=1)
edge = cv.erode(edge, None, iterations=1)


cnt = cv.findContours(edge.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
cnt = imutils.grab_contours(cnt)

##(cnt, _) = contours.sort_contours(cnt)

for k in cnt:
    if cv.contourArea(k) < 1000:
        continue

    main = img.copy()
    box = cv.minAreaRect(k)
    box = cv.boxPoints(box)
    box = np.array(box, dtype="int")

    box = perspective.order_points(box)
    cv.drawContours(main, [box.astype("int")], -1, (0, 255, 0), 1)

    for (x, y) in box:
        cv.circle(main, (int(x), int(y)), 5, (0, 0, 255), -1)

    cv.imshow("main", main)
    cv.waitKey(0)



##cv.imshow("img", img)
##cv.imshow("mask", mask)
##cv.imshow("mask2", mask2)
##cv.imshow("edge", edged)
##cv.imshow("maskClose", maskClose)
##cv.imshow("maskOpen", maskOpen)

##cv.waitKey(0)
cv.destroyAllWindows()
yanabeca
  • 35
  • 1
  • 1
  • 7
  • Can you show expected result? – Alderven Mar 01 '19 at 20:06
  • I would like to be able to find every single rectangle shown in the screenshot so I can work in them seperately to measure water depth. [example](http://prntscr.com/ms0xja) – yanabeca Mar 01 '19 at 21:19

1 Answers1

2

Dont make it too complicated. I used some of your code.

PS: I can only help you up to a point where you can focus on trying to measure the water levels. But I will give you a tip at the end

import numpy as np
import cv2

def show(img):
    cv2.imshow('a',img)
    cv2.waitKey()
    cv2.destroyAllWindows()


mask = cv2.imread("azC2r.jpg",0)
img = cv2.imread("azC2r.jpg")
print('Image shape: {}'.format(img.shape))

ret,thresh = cv2.threshold(mask,50,255,cv2.THRESH_BINARY)

thresh = cv2.blur(thresh,(7,7))
thresh[thresh<254]=0


kernel = np.ones((7,7))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
kernel = np.ones((9,9))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)


im2, contours,_ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

contours = [cnt for cnt in contours if cv2.contourArea(cnt)<100000]

cv2.drawContours(img, contours, -1, (0,255,0), 3)

# Thank to this snippet to
# https://stackoverflow.com/questions/37912928/fill-the-outside-of-contours-opencv
stencil = np.zeros(img.shape).astype(img.dtype)
color = [255, 255, 255]
cv2.fillPoly(stencil, contours, color)
img = cv2.bitwise_and(img, stencil)
# END of snippet
img[np.where(np.all(img==[255,255,255],axis = 2))]=[0,0,0]


show(img)

Result enter image description here

What I did?: I will explain you in images.

Threshold on grayscale

Threshold

Blur on Threshold to maked lines filled

enter image description here

Everything that is not pure White [255,255,255] becomes [0,0,0]

enter image description here

Get rid of separate small particals with morphological trick

enter image description here

Draw contours of particular size - get rid of too big and too small ones

enter image description here

At the end with cv2.polly get rid of everything outside of countours and turn it to black. See first image for the result

As far as water level measurements I dont really know and I dont want to dig in it, but maybe you can play with sobely

sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)

enter image description here

Martin
  • 3,333
  • 2
  • 18
  • 39
  • Oh wow that is actually so much better and simpler, thank you. I will look into sobely as soon as I get some images with no reflection. – yanabeca Mar 01 '19 at 22:40