Read water without sending
This commit is contained in:
parent
7aa69e63ac
commit
738161b0da
|
|
@ -1,239 +0,0 @@
|
||||||
import math
|
|
||||||
import sys
|
|
||||||
import cv2
|
|
||||||
import os
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
|
|
||||||
class WaterMeter(object):
|
|
||||||
|
|
||||||
def __init__(self, url, img=None, debug=False):
|
|
||||||
self.url = url
|
|
||||||
self.img = img
|
|
||||||
self.debug = debug
|
|
||||||
|
|
||||||
def read(self):
|
|
||||||
if self.img:
|
|
||||||
img = cv2.imread(self.img)
|
|
||||||
return img
|
|
||||||
|
|
||||||
def loop(self):
|
|
||||||
img = self.read()
|
|
||||||
needle = self.find_red_needle(img)
|
|
||||||
#needle = self.get_contours(needle, img)
|
|
||||||
dials = self.find_circle(img)
|
|
||||||
|
|
||||||
for dial in dials:
|
|
||||||
ulx = int(dial[0])
|
|
||||||
uly = int(dial[1])
|
|
||||||
radius = int(dial[2]) * 3
|
|
||||||
cut = img[uly - radius: uly + radius,
|
|
||||||
ulx - radius: ulx + radius]
|
|
||||||
needle_cut = needle[uly - radius: uly + radius,
|
|
||||||
ulx - radius: ulx + radius]
|
|
||||||
self.findAngle(cut, needle_cut, (radius, radius), radius * 2)
|
|
||||||
if self.debug:
|
|
||||||
cv2.imshow('image', img)
|
|
||||||
cv2.waitKey(0)
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def line(p1, p2):
|
|
||||||
A = (p1[1] - p2[1])
|
|
||||||
B = (p2[0] - p1[0])
|
|
||||||
C = (p1[0] * p2[1] - p2[0] * p1[1])
|
|
||||||
return A, B, -C
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def intersection(L1, L2):
|
|
||||||
D = L1[0] * L2[1] - L1[1] * L2[0]
|
|
||||||
Dx = L1[2] * L2[1] - L1[1] * L2[2]
|
|
||||||
Dy = L1[0] * L2[2] - L1[2] * L2[0]
|
|
||||||
if D != 0:
|
|
||||||
x = Dx / D
|
|
||||||
y = Dy / D
|
|
||||||
return x, y
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def find_circle(self, img):
|
|
||||||
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
||||||
gradient = cv2.HOUGH_GRADIENT
|
|
||||||
# Find suitable number by running detect_circle.py
|
|
||||||
circles = cv2.HoughCircles(img, gradient, 1.2, 40,
|
|
||||||
param1=50, param2=50, minRadius=50,
|
|
||||||
maxRadius=100)
|
|
||||||
if circles is not None:
|
|
||||||
circles = np.round(circles[0, :]).astype("int")
|
|
||||||
dials = []
|
|
||||||
for (x, y, r) in circles:
|
|
||||||
dials.append([x, y, r])
|
|
||||||
if self.debug:
|
|
||||||
# draw the circle
|
|
||||||
cv2.circle(img, (x, y), r, (0, 255, 0), 4)
|
|
||||||
# draw a rectangle in the middle
|
|
||||||
cv2.rectangle(img, (x - 5, y - 5), (x + 5, y + 5),
|
|
||||||
(0, 128, 255),
|
|
||||||
-1)
|
|
||||||
if self.debug:
|
|
||||||
cv2.imshow('circle', img)
|
|
||||||
cv2.waitKey(0)
|
|
||||||
|
|
||||||
return dials
|
|
||||||
|
|
||||||
def find_red_needle(self, img):
|
|
||||||
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
|
||||||
# Find suitable hsv number by running detect_hsv.py
|
|
||||||
lower_red_hue = self.create_hue_mask(hsv, [0, 100, 100], [10, 255, 255])
|
|
||||||
higher_red_hue = self.create_hue_mask(hsv, [170, 100, 100], [179, 255, 255])
|
|
||||||
|
|
||||||
mask = cv2.bitwise_or(lower_red_hue, higher_red_hue)
|
|
||||||
needle = cv2.GaussianBlur(mask, (5, 5), 0)
|
|
||||||
return needle
|
|
||||||
|
|
||||||
def get_contours(self, img, org):
|
|
||||||
ret, thresh = cv2.threshold(img, 127, 255, 0)
|
|
||||||
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE,
|
|
||||||
cv2.CHAIN_APPROX_SIMPLE)
|
|
||||||
|
|
||||||
if self.debug:
|
|
||||||
for contour in contours:
|
|
||||||
peri = cv2.arcLength(contour, True)
|
|
||||||
approx = cv2.approxPolyDP(contour, 0.04 * peri, True)
|
|
||||||
|
|
||||||
cv2.drawContours(org, [approx], -1, (0, 0, 255), 3)
|
|
||||||
|
|
||||||
#cv2.drawContours(org, contours, -1, (0, 255, 0), 3)
|
|
||||||
cv2.imshow('needle', org)
|
|
||||||
cv2.waitKey(0)
|
|
||||||
|
|
||||||
return contours
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def draw_line(lines, img):
|
|
||||||
for r, theta in lines[0]:
|
|
||||||
# Stores the value of cos(theta) in a
|
|
||||||
a = np.cos(theta)
|
|
||||||
|
|
||||||
# Stores the value of sin(theta) in b
|
|
||||||
b = np.sin(theta)
|
|
||||||
|
|
||||||
# x0 stores the value rcos(theta)
|
|
||||||
x0 = a * r
|
|
||||||
|
|
||||||
# y0 stores the value rsin(theta)
|
|
||||||
y0 = b * r
|
|
||||||
|
|
||||||
# x1 stores the rounded off value of (rcos(theta)-1000sin(theta))
|
|
||||||
x1 = int(x0 + 1000 * (-b))
|
|
||||||
|
|
||||||
# y1 stores the rounded off value of (rsin(theta)+1000cos(theta))
|
|
||||||
y1 = int(y0 + 1000 * (a))
|
|
||||||
|
|
||||||
# x2 stores the rounded off value of (rcos(theta)+1000sin(theta))
|
|
||||||
x2 = int(x0 - 1000 * (-b))
|
|
||||||
|
|
||||||
# y2 stores the rounded off value of (rsin(theta)-1000cos(theta))
|
|
||||||
y2 = int(y0 - 1000 * (a))
|
|
||||||
|
|
||||||
# cv2.line draws a line in img from the point(x1,y1) to (x2,y2).
|
|
||||||
# (0,0,255) denotes the colour of the line to be
|
|
||||||
# drawn. In this case, it is red.
|
|
||||||
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
|
|
||||||
|
|
||||||
return img
|
|
||||||
|
|
||||||
def findAngle(self, img, redimg, center, width):
|
|
||||||
### lines
|
|
||||||
edges = cv2.Canny(redimg, 100, 200, apertureSize=3)
|
|
||||||
lines = cv2.HoughLinesP(image=edges,
|
|
||||||
rho=1,
|
|
||||||
theta=np.pi / 90,
|
|
||||||
threshold=15,
|
|
||||||
minLineLength=width / 4,
|
|
||||||
maxLineGap=50)
|
|
||||||
|
|
||||||
tip = None
|
|
||||||
maxlen = 0
|
|
||||||
if lines is None:
|
|
||||||
print("No lines found")
|
|
||||||
|
|
||||||
cv2.imshow('error', img)
|
|
||||||
cv2.waitKey()
|
|
||||||
cv2.destroyAllWindows()
|
|
||||||
|
|
||||||
else:
|
|
||||||
pl = None
|
|
||||||
img = self.draw_line(lines, img)
|
|
||||||
cv2.imshow('error', img)
|
|
||||||
cv2.waitKey()
|
|
||||||
cv2.destroyAllWindows()
|
|
||||||
# print "%d lines" % len(lines)
|
|
||||||
for x in range(0, len(lines)):
|
|
||||||
for x1, y1, x2, y2 in lines[x]:
|
|
||||||
l = self.line([x1, y1], [x2, y2]);
|
|
||||||
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
|
|
||||||
# print "line: %d,%d %d,%d" % (x1, y1, x2, y2)
|
|
||||||
|
|
||||||
# see if it intersects any other line
|
|
||||||
for y in range(0, len(lines)):
|
|
||||||
for xx1, yy1, xx2, yy2 in lines[y]:
|
|
||||||
l2 = self.line([xx1, yy1], [xx2, yy2]);
|
|
||||||
if l2 is l:
|
|
||||||
continue
|
|
||||||
r = self.intersection(l, l2)
|
|
||||||
if r and r[0] > 0 and r[1] > 0:
|
|
||||||
dist = math.sqrt((r[0] - center[0]) ** 2 + (
|
|
||||||
r[1] - center[1]) ** 2)
|
|
||||||
# print "intersection %d,%d at distance %d" % ( r[0], r[1], dist)
|
|
||||||
# cv2.circle(img,(r[0],r[1]),2,(0,0,255),2)
|
|
||||||
if dist > maxlen and dist < width / 2:
|
|
||||||
tip = r
|
|
||||||
maxlen = dist
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# print "chosen intersection: %d,%d at distance %d" % ( tip[0], tip[1], maxlen)
|
|
||||||
#cv2.line(img, (tip[0], tip[1]), (center[0], center[1]), (255, 0, 255),
|
|
||||||
# 2)
|
|
||||||
|
|
||||||
xlen = tip[0] - center[0]
|
|
||||||
ylen = center[1] - tip[1]
|
|
||||||
rad = math.atan2(ylen, xlen)
|
|
||||||
deg = math.degrees(rad)
|
|
||||||
# print "angle deg:", deg
|
|
||||||
# print "angle rad:", rad
|
|
||||||
|
|
||||||
if deg < 0:
|
|
||||||
percent = (90 + abs(deg)) / 360
|
|
||||||
elif deg < 90:
|
|
||||||
percent = (90 - deg) / 360
|
|
||||||
else:
|
|
||||||
percent = (450 - deg) / 360
|
|
||||||
|
|
||||||
# print "percent", math.trunc(percent * 100)
|
|
||||||
string = "%d%%" % math.trunc(percent * 100)
|
|
||||||
cv2.putText(img, string, (center[0] - width / 5, center[1] - width / 3),
|
|
||||||
cv2.FONT_HERSHEY_SIMPLEX, 0.7,
|
|
||||||
(255, 255, 255), 2)
|
|
||||||
|
|
||||||
return math.trunc(percent * 100)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create_hue_mask(image, lower_color, upper_color):
|
|
||||||
lower = np.array(lower_color, np.uint8)
|
|
||||||
upper = np.array(upper_color, np.uint8)
|
|
||||||
|
|
||||||
# Create a mask from the colors
|
|
||||||
mask = cv2.inRange(image, lower, upper)
|
|
||||||
return mask
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
water_meter = WaterMeter('', img='capture_1.jpg', debug=True)
|
|
||||||
water_meter.loop()
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,225 @@
|
||||||
|
import math
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
import imutils
|
||||||
|
from scipy.spatial import distance as dist
|
||||||
|
|
||||||
|
|
||||||
|
class WaterMeter(object):
|
||||||
|
|
||||||
|
def __init__(self, url, img=None, debug=False):
|
||||||
|
self.url = url
|
||||||
|
self.img = img
|
||||||
|
self.debug = debug
|
||||||
|
self.cap = None
|
||||||
|
self._quit = False
|
||||||
|
|
||||||
|
def _init_camera(self):
|
||||||
|
self.cap = cv2.VideoCapture()
|
||||||
|
self.cap.open(self.url)
|
||||||
|
|
||||||
|
if self.cap is None:
|
||||||
|
print("Can't access camera")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not self.cap.isOpened():
|
||||||
|
print("Can't open camera")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.cap
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
|
||||||
|
img = cv2.imread(self.img)
|
||||||
|
self.get_degree(img)
|
||||||
|
|
||||||
|
def loop(self):
|
||||||
|
bad_frame = 0
|
||||||
|
while not self._quit:
|
||||||
|
if self._quit:
|
||||||
|
break
|
||||||
|
|
||||||
|
if bad_frame > 100:
|
||||||
|
if self.cap is not None:
|
||||||
|
self.cap.release()
|
||||||
|
self.cap = None
|
||||||
|
bad_frames = 0
|
||||||
|
|
||||||
|
# initializing connection to camera
|
||||||
|
if self.cap is None:
|
||||||
|
self._init_camera()
|
||||||
|
|
||||||
|
ret, current_frame = self.cap.read()
|
||||||
|
# the connection broke, or the stream came to an end
|
||||||
|
if (not ret) or (current_frame is None):
|
||||||
|
# ToDo Logging error frame
|
||||||
|
bad_frame += 1
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
bad_frame = 0
|
||||||
|
|
||||||
|
degree, img = self.get_degree(current_frame)
|
||||||
|
if degree < 0:
|
||||||
|
percent = (90 + abs(degree)) / 360
|
||||||
|
elif degree < 90 and degree != -1:
|
||||||
|
percent = (90 - degree) / 360
|
||||||
|
else:
|
||||||
|
percent = (450 - degree) / 360
|
||||||
|
print(f"Degree is {degree} and percent is {percent * 100}")
|
||||||
|
cv2.imshow("Degree", current_frame)
|
||||||
|
cv2.waitKey(1)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self._quit = True
|
||||||
|
|
||||||
|
if self.cap is not None:
|
||||||
|
self.cap.release()
|
||||||
|
|
||||||
|
def get_degree(self, frame):
|
||||||
|
img = frame
|
||||||
|
dials = self.find_circle(img)
|
||||||
|
# Init value out of range
|
||||||
|
deg = -1
|
||||||
|
dial_one_litre = []
|
||||||
|
if not dials:
|
||||||
|
return -1, img
|
||||||
|
for dial in dials:
|
||||||
|
# Using only one circle (1 litre) left bottom
|
||||||
|
if not dial_one_litre:
|
||||||
|
dial_one_litre = dial
|
||||||
|
if dial[0] < dial_one_litre[0]:
|
||||||
|
dial_one_litre = dial
|
||||||
|
|
||||||
|
ulx = int(dial_one_litre[0])
|
||||||
|
uly = int(dial_one_litre[1])
|
||||||
|
radius = int(dial_one_litre[2])
|
||||||
|
# Only using image in dial
|
||||||
|
cut = img[uly - radius: uly + radius, ulx - radius: ulx + radius]
|
||||||
|
|
||||||
|
# Find needle in dial
|
||||||
|
needle = self.find_red_needle(cut)
|
||||||
|
extreme_points = self.get_contours(needle, cut)
|
||||||
|
if not extreme_points:
|
||||||
|
return -1, img
|
||||||
|
deg, point = self.find_angle(extreme_points, cut)
|
||||||
|
cv2.line(img,
|
||||||
|
((ulx - radius) + point['x'], uly - radius + point['y']),
|
||||||
|
(ulx, uly), (0, 255, 255), 2)
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
cv2.imshow('image', img)
|
||||||
|
cv2.waitKey(0)
|
||||||
|
return deg, img
|
||||||
|
|
||||||
|
def find_circle(self, img):
|
||||||
|
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||||
|
gradient = cv2.HOUGH_GRADIENT
|
||||||
|
# Find suitable number by running detect_circle.py
|
||||||
|
circles = cv2.HoughCircles(img, gradient, dp=1.26, minDist=42,
|
||||||
|
param1=52, param2=43, minRadius=67,
|
||||||
|
maxRadius=99)
|
||||||
|
if circles is not None:
|
||||||
|
circles = np.round(circles[0, :]).astype("int")
|
||||||
|
dials = []
|
||||||
|
for (x, y, r) in circles:
|
||||||
|
dials.append([x, y, r])
|
||||||
|
if self.debug:
|
||||||
|
# draw the circle
|
||||||
|
cv2.circle(img, (x, y), r, (0, 255, 0), 4)
|
||||||
|
# draw a rectangle in the middle
|
||||||
|
cv2.rectangle(img, (x - 5, y - 5), (x + 5, y + 5),
|
||||||
|
(0, 128, 255),
|
||||||
|
-1)
|
||||||
|
if self.debug:
|
||||||
|
cv2.imshow('circle', img)
|
||||||
|
cv2.waitKey(0)
|
||||||
|
|
||||||
|
return dials
|
||||||
|
return None
|
||||||
|
|
||||||
|
def find_red_needle(self, img):
|
||||||
|
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
||||||
|
# Find suitable hsv number by running detect_hsv.py
|
||||||
|
lower_red_hue = self.create_hue_mask(hsv, [0, 100, 100], [10, 255, 255])
|
||||||
|
higher_red_hue = self.create_hue_mask(hsv, [170, 80, 110], [179, 255, 255])
|
||||||
|
|
||||||
|
mask = cv2.bitwise_or(lower_red_hue, higher_red_hue)
|
||||||
|
needle = cv2.GaussianBlur(mask, (5, 5), 0)
|
||||||
|
if self.debug:
|
||||||
|
cv2.imshow('circle', needle)
|
||||||
|
cv2.waitKey(0)
|
||||||
|
return needle
|
||||||
|
|
||||||
|
def get_contours(self, img, org):
|
||||||
|
ret, thresh = cv2.threshold(img, 127, 255, 0)
|
||||||
|
contours = cv2.findContours(thresh, cv2.RETR_TREE,
|
||||||
|
cv2.CHAIN_APPROX_SIMPLE)
|
||||||
|
cnts = imutils.grab_contours(contours)
|
||||||
|
try:
|
||||||
|
c = max(cnts, key=cv2.contourArea)
|
||||||
|
except ValueError:
|
||||||
|
print("Not finding any needle")
|
||||||
|
timestr = time.strftime("%Y%m%d-%H%M%S")
|
||||||
|
cv2.imwrite('error_neddle' + timestr + '.png', img)
|
||||||
|
return None
|
||||||
|
# determine the most extreme points along the contour
|
||||||
|
ext_left = tuple(c[c[:, :, 0].argmin()][0])
|
||||||
|
ext_right = tuple(c[c[:, :, 0].argmax()][0])
|
||||||
|
ext_top = tuple(c[c[:, :, 1].argmin()][0])
|
||||||
|
ext_bot = tuple(c[c[:, :, 1].argmax()][0])
|
||||||
|
|
||||||
|
extreme_points = (ext_left, ext_right, ext_top, ext_bot)
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
cv2.drawContours(org, [c], -1, (0, 255, 255), 2)
|
||||||
|
cv2.circle(org, ext_left, 8, (0, 0, 255), -1)
|
||||||
|
cv2.circle(org, ext_right, 8, (0, 255, 0), -1)
|
||||||
|
cv2.circle(org, ext_top, 8, (255, 0, 0), -1)
|
||||||
|
cv2.circle(org, ext_bot, 8, (255, 255, 0), -1)
|
||||||
|
# show the output image
|
||||||
|
cv2.imshow("Image", org)
|
||||||
|
cv2.waitKey(0)
|
||||||
|
|
||||||
|
return extreme_points
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_hue_mask(image, lower_color, upper_color):
|
||||||
|
lower = np.array(lower_color, np.uint8)
|
||||||
|
upper = np.array(upper_color, np.uint8)
|
||||||
|
|
||||||
|
# Create a mask from the colors
|
||||||
|
mask = cv2.inRange(image, lower, upper)
|
||||||
|
return mask
|
||||||
|
|
||||||
|
def find_angle(self, extreme_points, cut):
|
||||||
|
length_from_centre = {}
|
||||||
|
height, width, _ = cut.shape
|
||||||
|
|
||||||
|
for (x, y) in extreme_points:
|
||||||
|
D = dist.euclidean((x, y), (int(height/2), int(width/2)))
|
||||||
|
if not length_from_centre:
|
||||||
|
length_from_centre = {'x': x, 'y': y, 'distance': D}
|
||||||
|
elif D > length_from_centre['distance']:
|
||||||
|
length_from_centre = {'x': x, 'y': y, 'distance': D}
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
cv2.line(cut, (length_from_centre['x'], length_from_centre['y']),
|
||||||
|
(int(height / 2), int(width / 2)), (0, 0, 255), 2)
|
||||||
|
cv2.imshow('length', cut)
|
||||||
|
cv2.waitKey(0)
|
||||||
|
|
||||||
|
rad = math.atan2(length_from_centre['y'], length_from_centre['x'])
|
||||||
|
deg = math.degrees(rad)
|
||||||
|
|
||||||
|
return deg, length_from_centre
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
water_meter = WaterMeter('http://10.0.0.22:81', img='capture_4.png', debug=False)
|
||||||
|
water_meter.loop()
|
||||||
|
#water_meqter.read()
|
||||||
|
|
||||||
Loading…
Reference in New Issue