frame.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. #!/usr/bin/python3
  2. # -*- coding: utf-8 -*-
  3. import cv2
  4. import numpy as np
  5. import math
  6. import pytesseract
  7. def frame_grid(frame, camParams):
  8. h, w = frame.shape[:2]
  9. cx = int(w/2)
  10. cy = int(h/2)
  11. color = (0, 0, 255)
  12. if camParams['grid']:
  13. frame = cv2.line(frame, (cx, 0), (cx, h), color, thickness=1, lineType=8)
  14. frame = cv2.line(frame, (0, cy), (w, cy), color, thickness=1, lineType=8)
  15. if camParams['marker']:
  16. ppmm_h = 10*camParams['ppmm']/2
  17. frame = cv2.rectangle(frame, (int(cx-ppmm_h), int(cy-ppmm_h)), (int(cx+ppmm_h), int(cy+ppmm_h)), color, thickness=1)
  18. return frame
  19. def distance(x1, y1, x2, y2):
  20. dist = math.sqrt((x2-x1)**2 + (y2-y1)**2)
  21. return dist
  22. def rect_in_circle(cX, cY, objects):
  23. for item in objects:
  24. if distance(cX, cY, item['x'], item['y']) < item['r']:
  25. return True
  26. return False
  27. def top_frame_process(frame, camParams, cvParams, cvSettings):
  28. objects = {}
  29. # Convert to grayscale
  30. gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  31. # Correct Contrast
  32. gray=cv2.convertScaleAbs(gray, alpha=cvSettings['contrast'], beta=cvSettings['brightness'])
  33. # Blur
  34. #gray_blurred = cv2.blur(gray, (1, 1))
  35. gray_blurred = cv2.blur(gray, cvSettings['blur'])
  36. #thresh = cv2.adaptiveThreshold(gray_blurred,255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY,11,2)
  37. thresh = cv2.adaptiveThreshold(gray_blurred,255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, cvSettings['adaptiveThreshold_blockSize'], cvSettings['adaptiveThreshold_C'])
  38. if cvParams['img_type'] == 0:
  39. frameDisplay = frame
  40. else:
  41. frameDisplay = thresh
  42. ################
  43. # Detect circles
  44. ################
  45. objects['circle'] = []
  46. detected_circles = cv2.HoughCircles(gray_blurred,
  47. cv2.HOUGH_GRADIENT,
  48. dp=1.0,
  49. minDist = cvParams['circle']['dist-min'], # Минимальное расстояние между центрами кругов
  50. param1=300, # Порог для градиентного детектора
  51. param2=20, # Порог для акцепта круга
  52. minRadius = cvParams['circle']['r-min'], # Минимальный радиус круга
  53. maxRadius = cvParams['circle']['r-max']
  54. )
  55. # Draw circles that are detected.
  56. if detected_circles is not None:
  57. # Convert the circle parameters a, b and r to integers.
  58. detected_circles = np.uint16(np.around(detected_circles))
  59. for pt in detected_circles[0, :]:
  60. a, b, r = pt[0], pt[1], pt[2]
  61. rows,cols = frame.shape[:2]
  62. dx = cols/2 - a.astype(float)
  63. dy = rows/2 - b.astype(float)
  64. # Draw the circumference of the circle.
  65. frameDisplay = cv2.circle(frameDisplay, (a, b), r, (0, 255, 0), 2)
  66. # Draw a small circle (of radius 1) to show the center.
  67. frameDisplay = cv2.circle(frameDisplay, (a, b), 1, (0, 0, 255), 2)
  68. #print(a, b)
  69. objects['circle'].append({'x': a.astype(float), 'y': b.astype(float), 'dx': dx, 'dy': dy, 'r': r.astype(float)})
  70. ################
  71. ################
  72. # Detect rectangle
  73. ################
  74. objects['rect'] = []
  75. #contours, hierarchy = cv2.findContours(thresh, 1, 2)
  76. contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
  77. if contours is not None:
  78. for cnt in contours:
  79. #x1,y1 = cnt[0][0]
  80. #approx = cv2.approxPolyDP(cnt, 0.1*cv2.arcLength(cnt, True), True)
  81. approx = cv2.approxPolyDP(cnt, 0.01 * cv2.arcLength(cnt, True), True)
  82. rect = cv2.minAreaRect(approx)
  83. if len(approx) > 3:
  84. x, y, w, h = cv2.boundingRect(approx)
  85. #(x, y), (w, h), angle = rect
  86. #x, y, w, h = cv2.boundingRect(rect)
  87. #if (w > 20) and (w < 50):
  88. if (w > cvParams['rectangle']['w-min']) and (w < cvParams['rectangle']['w-max']) and (h > cvParams['rectangle']['h-min']) and (h < cvParams['rectangle']['h-max']):
  89. #rect = cv2.minAreaRect(approx)
  90. box = cv2.boxPoints(rect)
  91. box = np.intp(box)
  92. #frameDisplay = cv2.drawContours(frameDisplay, [box], 0, (0,0,255), 2)
  93. #M = cv2.moments(cnt)
  94. #cX = int(M["m10"] / M["m00"])
  95. #cY = int(M["m01"] / M["m00"])
  96. (center_x, center_y) = rect[0]
  97. cX = int(center_x)
  98. cY = int(center_y)
  99. #cv2.circle(frameDisplay, (cX, cY), 1, (0, 255, 0), 2)
  100. rows,cols = frame.shape[:2]
  101. [vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01)
  102. #lefty = int((-x*vy/vx) + y)
  103. #righty = int(((cols-x)*vy/vx)+y)
  104. #cv2.line(frameDisplay,(cols-1,righty),(0,lefty),(0,255,0),2)
  105. dx = cols/2 - cX
  106. dy = rows/2 - cY
  107. #text1 = "dx: {dx:.2f}, dy: {dy:.2f}".format(dx=dx, dy=dy)
  108. #frameDisplay = cv2.putText(frameDisplay, text1, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 1)
  109. x_axis = np.array([1, 0]) # unit vector in the same direction as the x axis
  110. your_line = np.array([vx, vy]) # unit vector in the same direction as your line
  111. dot_product = np.dot(x_axis, your_line)
  112. angle_2_x = np.arccos(dot_product)
  113. angle = math.degrees(angle_2_x[0])
  114. #print (angle)
  115. #text2 = "angle: {angle:.2f}".format(angle = 2.18)
  116. #frameDisplay = cv2.putText(frameDisplay, text2, (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 1)
  117. if rect_in_circle(cX, cY, objects['circle']) == False :
  118. frameDisplay = cv2.drawContours(frameDisplay, [box], 0, (0,0,255), 2)
  119. frameDisplay = cv2.circle(frameDisplay, (cX, cY), 1, (0, 255, 0), 2)
  120. objects['rect'].append({'cx': cX, 'cy': cY, 'dx': dx, 'dy': dy, 'angle': angle, 'box': box.tolist()})
  121. ################
  122. ################
  123. # Detect Text
  124. ################
  125. # Simple image to string
  126. if cvParams['text'] == True:
  127. objects['text'] = []
  128. data = pytesseract.image_to_data(frame, output_type='dict')
  129. boxes = len(data['level'])
  130. for i in range(boxes ):
  131. (x, y, w, h, text) = (data['left'][i], data['top'][i], data['width'][i], data['height'][i], data['text'][i])
  132. if (text != ""):
  133. print (x, y, w, h, text)
  134. frameDisplay = cv2.rectangle(frameDisplay, (x, y), (x+w, y+h), (200, 255, 200), 3)
  135. objects['text'].append({'x': x, 'y': y, 'w': w, 'h':h, 'text': text})
  136. return frameDisplay, objects
  137. def bottom_frame_process(frame_src, camParams, cvParams, cvSettings, negative=False):
  138. objects = {}
  139. frame = frame_src.copy()
  140. frame[:, :, 2] = 255
  141. frame[:, :, 0] = 255
  142. if negative == True:
  143. frame = cv2.bitwise_not(frame)
  144. # Correct Contrast
  145. frame = cv2.convertScaleAbs(frame, alpha=cvSettings['contrast'], beta=cvSettings['brightness'])
  146. # Blur
  147. #gray_blurred = cv2.blur(gray, (1, 1))
  148. blurred = cv2.blur(frame, cvSettings['blur'])
  149. # Convert to grayscale
  150. gray_blurred = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
  151. thresh = cv2.adaptiveThreshold(gray_blurred,255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, cvSettings['adaptiveThreshold_blockSize'], cvSettings['adaptiveThreshold_C'])
  152. if cvParams['img_type'] == 0:
  153. frameDisplay = frame_src
  154. else:
  155. frameDisplay = thresh
  156. #contours, hierarchy = cv2.findContours(thresh, 1, 2)
  157. # RETR_EXTERNAL, RETR_LIST, RETR_CCOMP, RETR_TREE
  158. # CHAIN_APPROX_NONE, CHAIN_APPROX_SIMPLE
  159. contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
  160. rows, cols = frame.shape[:2]
  161. if contours is not None:
  162. min_d = 99999
  163. res_contour = None
  164. res_aprox = None
  165. screenCx = cols/2
  166. screenCy = rows/2
  167. for cnt in contours:
  168. approx = cv2.approxPolyDP(cnt, 0.0*cv2.arcLength(cnt, True), True)
  169. x, y, w, h = cv2.boundingRect(approx)
  170. if w > cvParams['contour']['w-min'] and w < cvParams['contour']['w-max'] and h > cvParams['contour']['h-min'] and h < cvParams['contour']['h-max']:
  171. M = cv2.moments(cnt)
  172. cX = int(M["m10"] / M["m00"])
  173. cY = int(M["m01"] / M["m00"])
  174. dX = screenCx - cX
  175. dY = screenCy - cY
  176. dist = math.sqrt(dX**2 + dY**2)
  177. # Find the contour closest to the center
  178. if dist < w/2 and dist < h/2:
  179. if dist < min_d:
  180. min_d = dist
  181. res_contour = cnt
  182. res_aprox = approx
  183. if res_contour is not None:
  184. rect = cv2.minAreaRect(res_aprox)
  185. box = cv2.boxPoints(rect)
  186. # Draw contour
  187. box = np.intp(box)
  188. frameDisplay = cv2.drawContours(frameDisplay, [box], 0, (0,0,255), 2)
  189. # Draw Center
  190. #M = cv2.moments(res_contour)
  191. #cX = int(M["m10"] / M["m00"])
  192. #cY = int(M["m01"] / M["m00"])
  193. (center_x, center_y) = rect[0]
  194. cX = int(center_x)
  195. cY = int(center_y)
  196. frameDisplay = cv2.circle(frameDisplay, (cX, cY), 1, (0, 255, 0), 2)
  197. # Angle to rotate
  198. sorted_points = sorted(box, key=lambda x: x[1])[:2] # get two top points
  199. sorted_points = sorted(sorted_points, key=lambda x: x[0]) # sort points from left to right
  200. asin_top_line = (sorted_points[1][1] - sorted_points[0][1]) / (sorted_points[1][0] - sorted_points[0][0])
  201. angle_degrees = math.degrees(math.atan(asin_top_line))
  202. # Draw fitLine
  203. lefty = int(cY - cX*asin_top_line)
  204. righty = int(cY + (cols-cX)*asin_top_line)
  205. frameDisplay = cv2.line(frameDisplay,(cols-1,righty),(0,lefty),(0,255,0), 1)
  206. dx = (cols/2) - cX
  207. dy = (rows/2) - cY
  208. objects['object'] = {'dx': dx, 'dy': dy, 'angle_degrees': angle_degrees}
  209. else:
  210. objects['object'] = {'dx': None, 'dy': None, 'angle_degrees': None}
  211. ################
  212. # Detect Nozzle (circle)
  213. ################
  214. objects['nozzle'] = {'dx': None, 'dy': None}
  215. detected_circles = cv2.HoughCircles(gray_blurred,
  216. cv2.HOUGH_GRADIENT,
  217. dp=1.0,
  218. minDist = cvParams['circle']['dist-min'], # Минимальное расстояние между центрами кругов
  219. param1=300, # Порог для градиентного детектора
  220. param2=20, # Порог для акцепта круга
  221. minRadius = cvParams['circle']['r-min'], # Минимальный радиус круга
  222. maxRadius = cvParams['circle']['r-max']
  223. )
  224. # Draw circles that are detected.
  225. if detected_circles is not None:
  226. # Convert the circle parameters a, b and r to integers.
  227. detected_circles = np.uint16(np.around(detected_circles))
  228. # Find circle with minimal radius
  229. min_r = 9999
  230. min_circle = None
  231. for pt in detected_circles[0, :]:
  232. x, y, r = pt[0], pt[1], pt[2]
  233. if r < min_r:
  234. min_r = r
  235. min_circle = pt
  236. if min_circle is not None:
  237. x, y, r = min_circle[0], min_circle[1], min_circle[2]
  238. # Draw the circumference of the circle.
  239. frameDisplay = cv2.circle(frameDisplay, (x, y), r, (0, 255, 0), 2)
  240. # Draw a small circle (of radius 1) to show the center.
  241. frameDisplay = cv2.circle(frameDisplay, (x, y), 1, (0, 0, 255), 2)
  242. dx = (cols/2) - x
  243. dy = (rows/2) - y
  244. objects['nozzle'] = {'dx': dx, 'dy': dy}
  245. ################
  246. return frameDisplay, objects
  247. def frame_process(num, frame, camParams, cvParams, cvSettings):
  248. if num == 0:
  249. return top_frame_process(frame, camParams, cvParams, cvSettings)
  250. if num == 1:
  251. #return bottom_frame_process(frame, camParams, cvParams, cvSettings)
  252. frameDisplay, objects = bottom_frame_process(frame, camParams, cvParams, cvSettings)
  253. # If no found any objects and nozzle then try found it on the inverted (negative) image. May be object is white.
  254. if objects['object']['dx'] == None and objects['nozzle']['dx'] == None:
  255. frameDisplay, objects = bottom_frame_process(frame, camParams, cvParams, cvSettings, True)
  256. return frameDisplay, objects