def _md_preprocess_img(img, det_params): """Image preprocessing: grayscale conversion, edge filtering, and some minor denoising operations.""" gray = imutils.grayscale(img) #TODO #https://docs.opencv.org/3.4/d4/d73/tutorial_py_contours_begin.html #findcontours should find (was???) White on black! - didn't look into it, but both b-on-w and w-on-b worked similarly well # Need to check why both thresh_bin and thresh_bin_inv works! _, bw = cv2.threshold(gray, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU) wb = cv2.bitwise_not(bw) # Blur if needed if det_params.edge_blur_kernel_size > 0: bw = cv2.blur(bw.copy(), (det_params.edge_blur_kernel_size, det_params.edge_blur_kernel_size)) # Edge detection edges = cv2.Canny(bw, det_params.edge_canny_lower_thresh, det_params.edge_canny_upper_thresh, apertureSize=det_params.edge_sobel_aperture) # Close minor gaps via dilation if det_params.edge_dilation_kernel_size > 0: kernel = np.ones((det_params.edge_dilation_kernel_size, det_params.edge_dilation_kernel_size), np.uint8) edges = cv2.dilate(edges, kernel, iterations=1) return PreprocessingResult(original=img, gray=gray, bw=bw, wb=wb, edges=edges)
def hough_lines(img): g = imutils.grayscale(img) vis = img.copy() edges = cv2.Canny(g, 100, 200, apertureSize=3) #lines = cv2.HoughLines(edges,1,np.pi/180,50) # for rho,theta in lines[0]: # a = np.cos(theta) # b = np.sin(theta) # x0 = a*rho # y0 = b*rho # x1 = int(x0 + 1000*(-b)) # y1 = int(y0 + 1000*(a)) # x2 = int(x0 - 1000*(-b)) # y2 = int(y0 - 1000*(a)) # cv2.line(vis,(x1,y1),(x2,y2),(255,0,255),2) lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 20, minLineLength=50, maxLineGap=5) if lines is not None: for r in range(lines.shape[0]): # print(lines[r,:].shape, lines[r,:][0], lines[r,0,0], lines[r,:,:], lines[r,:]) x1, y1, x2, y2 = lines[r, :][0] # for x1, y1, x2, y2 in lines: cv2.line(vis, (x1, y1), (x2, y2), (255, 0, 255), 2) #imvis.imshow(edges, title='edges', wait_ms=10) imvis.imshow(vis, title='Hough', wait_ms=10)
def fld_lines(img): #FIXME requires opencv-contrib-python g = imutils.grayscale(img) fld = cv2.ximgproc.createFastLineDetector() lines = fld.detect(g) vis = img.copy() vis = fld.drawSegments(vis, lines) imvis.imshow(vis, title='FLD lines', wait_ms=10)
def mser(img): g = imutils.grayscale(img) vis = img.copy() mser = cv2.MSER_create(_min_area=1000) regions, _ = mser.detectRegions(g) for p in regions: xmax, ymax = np.amax(p, axis=0) xmin, ymin = np.amin(p, axis=0) cv2.rectangle(vis, (xmin, ymax), (xmax, ymin), (0, 255, 0), 1) imvis.imshow(vis, title='mser', wait_ms=-1)
def contour(img): g = imutils.grayscale(img) _, g = cv2.threshold(g, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # g = imutils.gaussian_blur(g, 1) # g = cv2.blur(g, (3,3)) vis = imutils.ensure_c3(g) vis = img.copy() edges = cv2.Canny(g, 50, 200, apertureSize=3) kernel = np.ones((3, 3), np.uint8) edges = cv2.dilate(edges, kernel, iterations=1) cnts = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) #cv2.CHAIN_APPROX_SIMPLE) cnts = cnts[0] if len(cnts) == 2 else cnts[1] #https://docs.opencv.org/3.4/d4/d73/tutorial_py_contours_begin.html # White on black! approximated_polygons = list() for cnt in cnts: # get simplified convex hull epsilon = 0.05 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) hull = cv2.convexHull(approx) # cv2.drawContours(vis, [approx], 0, (0,255,0), 3) approximated_polygons.append({ 'hull': hull, 'approx': approx, 'hull_area': cv2.contourArea(hull), 'corners': len(hull) }) def _key(a): return a['hull_area'] approximated_polygons.sort(key=_key, reverse=True) i = 0 for approx in approximated_polygons: #cv2.drawContours(vis, [approx['cnt']], 0, (0,255,0) if i < 10 else (255,0,0), 3) cv2.drawContours(vis, [approx['hull']], 0, (0, 255, 0) if 3 < approx['corners'] < 6 else (255, 0, 0), 3) i += 1 # if i < 15: # print('Largest', i, approx['approx'], approx['area']) # imvis.imshow(vis, title='contours', wait_ms=-1) imvis.imshow(vis, title='contours', wait_ms=-1)
def align(self, image, H0): self.H0 = H0 if self.verbose: vis = self._warp_current_image(image, np.eye(3)) imvis.imshow(vis, 'Initial Warp', wait_ms=10) curr_original_image = image.copy() working_image = imutils.grayscale(image) working_image = cv2.GaussianBlur(working_image, self.blur_kernel_size, 0) working_pyramid = img_utils.image_pyramid(working_image, self.num_pyramid_levels) H = np.eye(3, dtype=float) # Coarse-to-fine: for lvl in range(self.num_pyramid_levels): pyr_lvl = self.num_pyramid_levels - lvl - 1 H = self._process_in_layer(H, working_pyramid[pyr_lvl], self.template_pyramid[pyr_lvl], pyr_lvl) warped = self._warp_current_image(curr_original_image, H) return H, warped
def __init__(self, image, method=Method.ESM, num_pyramid_levels=4, blur_kernel_size=(5, 5), max_iterations=50, verbose=False, full_reference_image=None): # TODO if verbose is True, you must provide the full reference image, too! self.verbose = verbose self.full_reference_image = full_reference_image self.template_image = imutils.grayscale(image) self.height, self.width = image.shape[:2] self.H0 = None self.H_gt = None self.method = method self.max_iterations = max_iterations self.num_pyramid_levels = num_pyramid_levels self.blur_kernel_size = blur_kernel_size self.template_image = cv2.GaussianBlur(self.template_image, self.blur_kernel_size, 0) self.template_pyramid = img_utils.image_pyramid(self.template_image, self.num_pyramid_levels) self.sl3_bases = _get_SL3_bases() self.Jg = _compute_Jg(self.sl3_bases) self.JwJg = None self.dxdys = None # Stores precomputed gradients for each pyramid level self.Js = None self.Hs = None self._precompute()
def _compute_calibration_template(self): """Precomputes the calibration template image and reference points.""" # Render full template tpl_full = imutils.grayscale( self.render_image()) # The rendered PNG will have 3-channels tpl_h, tpl_w = tpl_full.shape[:2] # Compute center marker position relative to the full target relrect_marker_tight, _ = self._get_relative_marker_rect(0) marker_roi_tight = Rect(left=tpl_w * relrect_marker_tight.left, top=tpl_h * relrect_marker_tight.top, width=tpl_w * relrect_marker_tight.width, height=tpl_h * relrect_marker_tight.height) # Compute the corresponding corners for homography estimation (full # image warping) and ensure that they are sorted in CCW order ref_pts_marker_absolute = sort_points_ccw([ marker_roi_tight.top_left, marker_roi_tight.bottom_left, marker_roi_tight.bottom_right, marker_roi_tight.top_right ]) # Crop the central marker (with some small margin) and resize # to the configured template size relrect_marker_padded, rmr_offset = \ self._get_relative_marker_rect(self.template_marker_margin_mm) marker_roi_padded = Rect(left=tpl_w * relrect_marker_padded.left, top=tpl_h * relrect_marker_padded.top, width=tpl_w * relrect_marker_padded.width, height=tpl_h * relrect_marker_padded.height) tpl_marker = cv2.resize(imutils.crop(tpl_full, marker_roi_padded.int_repr()), dsize=(self.template_marker_size_px, self.template_marker_size_px), interpolation=cv2.INTER_CUBIC) # Compute the reference corners for homography estimation and ensure that # they are sorted in CCW order ref_pts_tpl_marker = sort_points_ccw([ Point(x=rmr_offset.x * tpl_marker.shape[1], y=rmr_offset.y * tpl_marker.shape[0]), Point(x=tpl_marker.shape[1] - rmr_offset.x * tpl_marker.shape[1], y=rmr_offset.y * tpl_marker.shape[0]), Point(x=tpl_marker.shape[1] - rmr_offset.x * tpl_marker.shape[1], y=tpl_marker.shape[0] - rmr_offset.y * tpl_marker.shape[0]), Point(x=rmr_offset.x * tpl_marker.shape[1], y=tpl_marker.shape[0] - rmr_offset.y * tpl_marker.shape[0]) ]) # Compute the circle template to locate the actual calibration reference # points (i.e. the grid points) relrect_circle, ref_pts_circle_relative = self._get_relative_marker_circle_v2( ) circle_roi = Rect(left=tpl_w * relrect_circle.left, top=tpl_h * relrect_circle.top, width=tpl_w * relrect_circle.width, height=tpl_h * relrect_circle.height) # Ensure the ROI is even (simplifies using the correlation results later on) circle_roi.ensure_odd_size() tpl_circle = imutils.crop(tpl_full, circle_roi.int_repr()) ref_pts_tpl_circle = [ Point(x=pt.x * tpl_circle.shape[1], y=pt.y * tpl_circle.shape[0]) for pt in ref_pts_circle_relative ] # Compute expected size of a circle in the image template dia_circle_px = self.dia_circles_mm / self.target_width_mm * tpl_full.shape[ 1] ###DEBUG VISUALS # vis_tpl_circle = tpl_circle.copy() # foo = imutils.ensure_c3(tpl_full.copy()) # FIXME REMOVE # cv2.rectangle(foo, circle_roi.int_repr(), color=(0, 250, 0), thickness=3) # for pt in ref_pts_tpl_circle: # cv2.circle(vis_tpl_circle, tuple(pt.int_repr()), radius=3, color=(200, 0, 0), thickness=1) # imvis.imshow(foo, 'FOO', wait_ms=10) # print(tpl_circle.shape, ref_pts_tpl_circle[0]) # imvis.imshow(vis_tpl_circle, "CIRCLE????", wait_ms=-1) object.__setattr__( self, 'calibration_template', CalibrationTemplate( tpl_full=tpl_full, refpts_full_marker=ref_pts_marker_absolute, #TODO extract reference points!!!! grid tpl_cropped_marker=tpl_marker, refpts_cropped_marker=ref_pts_tpl_marker, tpl_cropped_circle=tpl_circle, refpts_cropped_circle=ref_pts_tpl_circle, dia_circle_px=dia_circle_px))