frame.py 12 KB

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