def process(self): intensity = self.intensity_spin.value() invert = self.invert_check.isChecked() equalize = self.equalize_check.isChecked() blue_mode = self.blue_combo.currentIndex() dx, dy = cv.spatialGradient(cv.cvtColor(self.image, cv.COLOR_BGR2GRAY)) if invert: dx = -dx dy = -dy red = ((dx.astype(np.float32) / np.max(np.abs(dx)) * 127) + 127).astype(np.uint8) green = ((dy.astype(np.float32) / np.max(np.abs(dy)) * 127) + 127).astype(np.uint8) if blue_mode == 0: blue = np.zeros_like(red) elif blue_mode == 1: blue = np.full_like(red, 255) elif blue_mode == 2: blue = normalize_mat(np.linalg.norm(cv.merge((red, green)), axis=2)) else: blue = None gradient = cv.merge([blue, green, red]) if intensity > 0: gradient = cv.LUT(gradient, create_lut(intensity, intensity)) if equalize: gradient = equalize_image(gradient) self.grad_viewer.update_processed(gradient)
def cv_SpatialGradient(input=('ImagePin', 0), ksize=('IntPin', 3), dx=(REF, ('ImagePin', 0)), dy=(REF, ('ImagePin', 0))): """ Blurs An image.""" x, y = cv2.spatialGradient(input.image, ksize) dx(x) dy(y)
def img_grad(img): dx = np.zeros(img.shape[:-1], dtype=np.int16) dy = dx.copy() for i in range(3): dx_curr, dy_curr = cv2.spatialGradient(img[:, :, i]) for (d, d_curr) in [(dx, dx_curr), (dy, dy_curr)]: idx = abs(d_curr) > abs(d) d[idx] = d_curr[idx] d_ver, d_hor = dx[1:, 1:], dy[1:, 1:] return d_ver, d_hor
def get_silhouette_idt_grad_from_mask(fg_mask: ndarray): """fg_mask is a boolean mask that should be True where it is the foreground, and False in background.""" fg_mask_uint = fg_mask.astype(np.uint8) * 255 silhouette = cv2.Laplacian(fg_mask_uint, cv2.CV_8U) silhouette_bool = silhouette > 0 pre_idt = np.where(silhouette_bool, 0, 255).astype(np.uint8) idt = image_distance_transform(pre_idt) idt = (idt / idt.max()) * 255 dx, dy = cv2.spatialGradient(idt.astype(np.uint8)) grad = np.stack((dx, dy), axis=-1) return silhouette_bool, idt, grad
def __init__(self, image, parent=None): super(GradientWidget, self).__init__(parent) self.intensity_spin = QSpinBox() self.intensity_spin.setRange(0, 100) self.intensity_spin.setValue(95) self.intensity_spin.setSuffix(self.tr(" %")) self.intensity_spin.setToolTip(self.tr("Tonality compression amount")) self.blue_combo = QComboBox() self.blue_combo.addItems([ self.tr("None"), self.tr("Flat"), self.tr("Abs"), self.tr("Norm") ]) self.blue_combo.setCurrentIndex(2) self.blue_combo.setToolTip(self.tr("Blue component inclusion mode")) self.invert_check = QCheckBox(self.tr("Invert")) self.invert_check.setToolTip(self.tr("Reverse lighting direction")) self.equalize_check = QCheckBox(self.tr("Equalize")) self.equalize_check.setToolTip(self.tr("Apply histogram equalization")) self.image = image self.viewer = ImageViewer(self.image, self.image) self.dx, self.dy = cv.spatialGradient( cv.cvtColor(self.image, cv.COLOR_BGR2GRAY)) self.process() self.intensity_spin.valueChanged.connect(self.process) self.blue_combo.currentIndexChanged.connect(self.process) self.invert_check.stateChanged.connect(self.process) self.equalize_check.stateChanged.connect(self.process) top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr("Intensity:"))) top_layout.addWidget(self.intensity_spin) top_layout.addWidget(QLabel(self.tr("Blue channel:"))) top_layout.addWidget(self.blue_combo) top_layout.addWidget(self.invert_check) top_layout.addWidget(self.equalize_check) top_layout.addStretch() main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout)
def FDO2(input): #First derivative operators - Sobel masks - Part II img = cv2.imread("eye.jpeg", cv2.IMREAD_GRAYSCALE) gx, gy = cv2.spatialGradient(img, ksize=3, borderType=cv2.BORDER_DEFAULT) g = np.abs(gx) + np.abs(gy) gx = scaleImage2_uchar(gx) gy = scaleImage2_uchar(gy) g = scaleImage2_uchar(g) while cv2.waitKey(1) != ord('q'): cv2.imshow("Original", img) cv2.imshow("New", g) cv2.imshow("Gx", gx) cv2.imshow("Gy", gy)
def simple_lucas_kanade(prev_img, next_img, prev_pts, win_size=25, mode='pyr'): ''' Realization of simple Lucas-Kanade method, without image pyramid Input: prev_img - np.array, grayscale image of previous frame of video next_img - np.array, grayscale image of current frame of video prev_pts - np.array, special points to be tracked win_size - int, window size for Lucas-Kanade method mode - str, could be 'pyr' or 'simple'. In 'pyr' mode returns points velocity, in 'simple' mode returns new points. Output: velocities or new_points - np.array, depends on choosen mode ''' # calc derivatives der_x, der_y = cv2.spatialGradient(next_img) der_t = cv2.subtract(next_img, prev_img) half = win_size // 2 # for every point calc v and u in window new_points = np.zeros_like(prev_pts) veloc = np.zeros_like(prev_pts) for i, point in enumerate(prev_pts): x_c, y_c = point[0][0], point[0][1] # get coords of window # ADDED 1 TO GET REAL WINDOW x_coords = np.clip([x_c - half, x_c + half + 1], 0, next_img.shape[1]) y_coords = np.clip([y_c - half, y_c + half + 1], 0, next_img.shape[0]) # x1, x2 = int(np.rint(x_coords[0])), int(np.rint(x_coords[1])) # y1, y2 = int(np.rint(y_coords[0])), int(np.rint(y_coords[1])) x1, x2 = int(x_coords[0]), int(x_coords[1]) y1, y2 = int(y_coords[0]), int(y_coords[1]) cur_dx = der_x[y1:y2, x1:x2] cur_dy = der_y[y1:y2, x1:x2] cur_dt = der_t[y1:y2, x1:x2] A = np.array([[np.sum(cur_dx**2), np.sum(cur_dx * cur_dy)], [np.sum(cur_dx * cur_dy), np.sum(cur_dy**2)]]) b = np.array([-np.sum(cur_dx * cur_dt), -np.sum(cur_dy * cur_dt)]).T v, u = np.linalg.pinv(A) @ b new_points[i][0] = [x_c + v, y_c + u] veloc[i][0] = [v, u] if mode == 'pyr': return veloc if mode == 'simple': return new_points
def fdo_sobel_masks_2(): img = cv2.imread('../../img/Lenna.png', cv2.IMREAD_GRAYSCALE) gx, gy = cv2.spatialGradient(img, ksize=3, borderType=cv2.BORDER_DEFAULT) g = np.abs(gx) + np.abs(gy) gx = scaleImage2_uchar(gx) gy = scaleImage2_uchar(gy) g = scaleImage2_uchar(g) cv2.imshow("Original", img) cv2.imshow("New", g) cv2.imshow("Gx", gx) cv2.imshow("Gy", gy) while True: if 0xFF & cv2.waitKey(1) == ord('q'): break cv2.destroyAllWindows()
def detect_inner_corners(hsv_white_only, img_id=0): # Parameters ########################### second_blur_size = 49 ignore_border_size = 3 corner_harris_block_size = 4 # Define valid intensity range for the median of a corner min_intensity_average = 170 max_intensity_average = 240 ######################################## # hsv_origin = hsv.copy() blur = make_gaussian_blurry(hsv_white_only, 5) double_blur = make_gaussian_blurry(blur, second_blur_size) # Sub-pixel: dst = cv2.cornerHarris(blur, corner_harris_block_size, 3, 0.04) dst = cv2.dilate(dst, None) ret, dst = cv2.threshold(dst, 0.01 * dst.max(), 255, 0) dst = np.uint8(dst) # find centroids ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst) # define the criteria to stop and refine the corners criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001) corners = cv2.cornerSubPix(blur, np.float32(centroids), (5, 5), (-1, -1), criteria) # Filter the corners number_of_corners = len(corners) x_min = np.array([ignore_border_size] * number_of_corners) x_max = np.array([IMG_HEIGHT - ignore_border_size] * number_of_corners) y_min = np.array([ignore_border_size] * number_of_corners) y_max = np.array([IMG_WIDTH - ignore_border_size] * number_of_corners) # Keep corners within the border limit corners_clipped_on_border = corners[np.logical_and( np.logical_and( np.greater(corners[:, 1], x_min), # Add top limit np.less(corners[:, 1], x_max) # Add bottom limit ), np.logical_and( np.greater(corners[:, 0], y_min), # Add left limit np.less(corners[:, 0], y_max) # Add right limit ))] corner_x = np.int0(corners_clipped_on_border[:, 1]) corner_y = np.int0(corners_clipped_on_border[:, 0]) number_of_corners = len(corner_x) min_intensity = np.array([min_intensity_average] * number_of_corners) max_intensity = np.array([max_intensity_average] * number_of_corners) # Filter out the corners that belong to an "outer corner" # and outliers that are inside the white area corners_clipped_on_intensity = corners_clipped_on_border[np.logical_and( np.greater(double_blur[corner_x, corner_y], min_intensity), # Add top limit np.less(double_blur[corner_x, corner_y], max_intensity) # Add bottom limit )] corner_x = np.int0(corners_clipped_on_intensity[:, 1]) corner_y = np.int0(corners_clipped_on_intensity[:, 0]) if np.ndim(corner_x) == 0: number_of_corners = 1 corners = np.array([[corner_x, corner_y]]) else: corners = np.stack((corner_x, corner_y), axis=1) number_of_corners = len(corners) # print "Number of corners:", number_of_corners if number_of_corners == 0 or number_of_corners > 4: print("Invalid number of corners") return None, None ###################### # Define the corners # if number_of_corners == 1: corner_0 = corners[0] if number_of_corners == 2: left_corner_id = np.argmin(corner_y) right_corner_id = 1 - left_corner_id corner_0 = corners[left_corner_id] corner_1 = corners[right_corner_id] if number_of_corners == 3: distances = np.array([ np.linalg.norm(corners[0] - corners[1]), np.linalg.norm(corners[1] - corners[2]), np.linalg.norm(corners[2] - corners[0]) ]) min_dist_id = np.argmin(distances) if min_dist_id == 0: relevant_corners = np.stack((corners[0], corners[1]), axis=0) elif min_dist_id == 1: relevant_corners = np.stack((corners[1], corners[2]), axis=0) elif min_dist_id == 2: relevant_corners = np.stack((corners[2], corners[0]), axis=0) else: print( "ERROR: In fn. detect_sub_pixecl_corners(); min_dist_id out of bounds" ) return None, None dist_0_1, dist_1_2, dist_2_0 = distances[0], distances[1], distances[2] # Pick the corner with the lowest y-coordinate left_corner_id = np.argmin(relevant_corners, axis=0)[1] right_corner_id = 1 - left_corner_id corner_0 = relevant_corners[left_corner_id] corner_1 = relevant_corners[right_corner_id] if number_of_corners == 4: # For the first corner, chose the corner closest to the top # This will belong to the top cross-bar top_corner_id = np.argmin(corner_x) top_corner = corners[top_corner_id] top_corner_stack = np.array([top_corner] * 3) rest_corners = np.delete(corners, top_corner_id, 0) dist = np.linalg.norm(rest_corners - top_corner_stack, axis=1) # For the second corner, chose the corner closest to top corner top_corner_closest_id = np.argmin(dist) top_corner_closest = rest_corners[top_corner_closest_id] relevant_corners = np.stack((top_corner, top_corner_closest), axis=0) # Choose the corner with the lowest y-coordinate as the first corner left_corner_id = np.argmin(relevant_corners, axis=0)[1] right_corner_id = 1 - left_corner_id corner_0 = relevant_corners[left_corner_id] corner_1 = relevant_corners[right_corner_id] ################################## # Define mid point and direction #' mid_point = None dir_vector = None # Calculate spatial gradient dy, dx = cv2.spatialGradient( blur) # Invert axis, since OpenCV operates with x=column, y=row if number_of_corners == 1: grad_0 = np.array( [dx[corner_0[0]][corner_0[1]], dy[corner_0[0]][corner_0[1]]]) grad_0_normalized = normalize_vector(grad_0) mid_point_offset = 5 # px-distance mid_point = np.int0(corner_0 + grad_0_normalized * mid_point_offset) dir_vector = grad_0_normalized else: grad_0 = np.array( [dx[corner_0[0]][corner_0[1]], dy[corner_0[0]][corner_0[1]]]) grad_1 = np.array( [dx[corner_1[0]][corner_1[1]], dy[corner_1[0]][corner_1[1]]]) grad_sum = grad_0 + grad_1 grad_sum_normalized = normalize_vector(grad_sum) cross_over_vector = corner_1 - corner_0 mid_point = np.int0(corner_0 + 0.5 * (cross_over_vector)) mid_value = double_blur[mid_point[0]][mid_point[1]] if number_of_corners == 2 and mid_value < 200: print "The two corners form the longitudinal bar" # Choose to focus on the left point v_long = corner_0 - corner_1 v_long_norm = normalize_vector(v_long) grad_0_normalized = normalize_vector(grad_0) if grad_0_normalized[0] < 0: long_bar_norm = np.array( [-cross_over_vector[1], cross_over_vector[0]]) else: long_bar_norm = np.array( [cross_over_vector[1], -cross_over_vector[0]]) long_bar_norm_normalized = normalize_vector(long_bar_norm) dist_to_top_edge = corner_0[0] dist_to_bottom_edge = IMG_HEIGHT - corner_0[0] dist_to_nearest_edge = min(dist_to_top_edge, dist_to_bottom_edge) mid_point_offset = dist_to_nearest_edge / 2 # px-distance mid_point = np.int0(corner_0 + long_bar_norm_normalized * mid_point_offset) dir_vector = v_long_norm elif number_of_corners != 1: if grad_sum_normalized[0] < 0: cross_over_norm = np.array( [-cross_over_vector[1], cross_over_vector[0]]) else: cross_over_norm = np.array( [cross_over_vector[1], -cross_over_vector[0]]) dir_vector = normalize_vector(cross_over_norm) ################### # Draw the result # direction_length = 20 direction_point = np.int0(mid_point + dir_vector * direction_length) # img_grad = hsv_origin.copy() # img_grad = cv2.arrowedLine(img_grad, (mid_point[1], mid_point[0]), # (direction_point[1], direction_point[0]), # color = (255,0,0), thickness = 2, tipLength = 0.5) # hsv_save_image(img_grad, "5_gradient") # hsv_save_image(img_grad, str(img_id) +"_gradient") return mid_point, corners
while cv2.waitKey(1) != ord('q'): cv2.imshow("Original", img) cv2.imshow("New", g) cv2.imshow("Gx", gx) cv2.imshow("Gy", gy) cv2.destroyAllWindows() #%% # First derivative operators - Sobel masks - Part II img = cv2.imread("img/lena.png", cv2.IMREAD_GRAYSCALE) gx, gy = cv2.spatialGradient(img, ksize=3, borderType=cv2.BORDER_DEFAULT) g = np.abs(gx) + np.abs(gy) gx = scaleImage2_uchar(gx) gy = scaleImage2_uchar(gy) g = scaleImage2_uchar(g) while cv2.waitKey(1) != ord('q'): cv2.imshow("Original", img) cv2.imshow("New", g) cv2.imshow("Gx", gx) cv2.imshow("Gy", gy) cv2.destroyAllWindows()
def task2(img, name=None): if img is not None: # Convert the image to greyscale grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Get the sobel gradient on both the X and Y directions, and sum them together sobX, sobY = cv2.spatialGradient(grey, None, None, 3) sob = np.square(sobX) + np.square(sobY) # Sum down each of the columns (axis=0) col_sum = np.sum(sob, axis=0) col_sum = col_sum / col_sum.max() # Normalise between 0 and 1.0 # Get the Lower and Upper X coordinates where an approximation of the sign lies minX, maxX = thresh_sweep(col_sum, 0.001, 0.22) # Extract a working area that is a little wider than the sign's best match area, ensuring valid bounds roi = grey[:, max(0, int(minX * 0.9)):min(grey.shape[1], int(maxX * 1.1))] roi_rgb = img[:, max(0, int(minX * 0.9)):min(grey.shape[1], int(maxX * 1.1))] # Adaptive threshold the macro ROI and then apply contouring roi_thresh = cv2.adaptiveThreshold(roi, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 25, 1) _, contours, heir = cv2.findContours(roi_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # First pass classification to find the ARROWS on the sign only. This step is to align the sign digit_regions = list() for cont in contours: # Return the (x,y) and width and height of a bounding rectangle for this contour x, y, w, h = cv2.boundingRect(cont) # Only consider regions that are the correct shape and aspect initially. if 40 < w * h < 5000 and 0.6 * w < h < 1.4 * w: # Get the underlying greyscale region for this bounding box for classification possible_digit = roi[y:y + h, x:x + w] classify, errors = cl.classify(possible_digit) # If the confidence of this digit is below the threshold and it is a Left arrow (10) or right arrow (11) if np.sum(errors) < 3.0 * cl.FEATURES and ( int(classify) == 10 or int(classify) == 11): #cv2.rectangle(roi_rgb, (x, y), (x+w, y+h), (0, 255, 255), thickness=2) # Calculate the full region with digits and arrow from the size of the arrow rx = max(0, x - int(3.9 * w)) ry = max(0, y - int(0.6 * h)) rw = int(4.9 * w) rh = int(2.2 * h) # Create a Digit Region and populate the data dr = DigitRegion(rx, ry, rw, rh, x, y, w, h, int(classify)) digit_regions.append(dr) #cv2.rectangle(roi_rgb, (rx, ry), (rx + rw, ry + rh), (0, 255, 0), thickness=1) #else: #cv2.rectangle(roi_rgb, (x, y), (x+w, y+h), (255, 0, 0), thickness=1) # Sort the list by the Y Coordinate digit_regions.sort(key=lambda dr: dr.ry) # Crop the sign down to only include the arrows and numbers region_output, cropped = crop_sign(roi_rgb, roi, digit_regions) # Upscale the image by 2 times and adaptive threshold it. cropped = cv2.resize(cropped, (cropped.shape[1] * 2, cropped.shape[0] * 2), cv2.INTER_LINEAR) up_thresh = cv2.adaptiveThreshold(cropped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21, 1) # Contour the upscaled threshold image _, contours, heir = cv2.findContours(up_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 3 channel greyscale for drawing coloured rectangles on warped_rgb = cv2.cvtColor(cropped, cv2.COLOR_GRAY2BGR) # Make a new list to store the new warped regions digit_regions = list() # Find the digit regions based on the location and size of the arrows for cont in contours: x, y, w, h = cv2.boundingRect(cont) # If the region area is within the threshold and that the width height ratio is acceptable. if (np.power(cropped.shape[1], 2)) // 40 < w * h < 5000 and 0.8 * w < h < 1.2 * w: possible_digit = cropped[y:y + h, x:x + w] classify, errors = cl.classify(possible_digit) # See if the digit is classified as an arrow and is sufficiently low in error. 10 = left, 11 = right if np.sum(errors) < 3.0 * cl.FEATURES and ( int(classify) == 10 or int(classify) == 11): cv2.rectangle(warped_rgb, (x, y), (x + w, y + h), (0, 255, 255), thickness=2) rx = 0 # Use the full sign width ry = max(0, y - int(.5 * h)) rw = cropped.shape[1] # Using the full sign width rh = int(2.0 * h) # Make it a DigitRegion and add it to the list dr = DigitRegion(rx, ry, rw, rh, x, y, w, h, classify) digit_regions.append(dr) cv2.rectangle(warped_rgb, (rx, ry), (rx + rw, ry + rh), (0, 255, 0), thickness=1) else: cv2.rectangle(warped_rgb, (x, y), (x + w, y + h), (255, 0, 0), thickness=1) # Order the List by ascending region Y coordinate digit_regions.sort(key=lambda dr: dr.ry) # Allocate a list to store the text on the sign, eg "113R" numbers_on_sign = list() # For each of the regions, sweep in from teh left hand side, checking which gives the best classification, # which indicates the true edge of the sign has been found. for dr in digit_regions: digits = cropped[dr.ry:dr.ry + dr.rh, dr.rx:rx + dr.rw] digits = cv2.threshold(digits, 120, 255, cv2.THRESH_OTSU)[1] width = int(dr.w * 1.2) # Noise reduction matches. Find the best image classification from left hand side inward bests = list() for sweep in range(0, width // 2): part = digits[:, sweep:width] part = crop_height(part) #cv2.imshow('part', part) classify, errors = cl.classify(part) bests.append([classify, np.sum(errors * errors), sweep]) # sort the best matches by their lowest distance classification bests.sort(key=lambda x: x[1]) # get the coordinate of the best digit, and crop the digit region digits = digits[:, int(bests[0][2]):] # first try left to right full height classification num1, num2, num3, num4 = classify_digits(digits) # Multiple attempts if for some reason the numebrs returned were illogical. E.g. an arrow where a digit is if not validate_classify(num1, num2, num3, num4): # Try only the top 70% of the digits image, all digits still form blocks here num1, num2, num3, num4 = classify_digits( digits[:int(digits.shape[0] * 0.70), :], digits) #if not validate_classify(num1, num2, num3, num4): # Try the full image in back to front order #num1, num2, num3, num4 = classify_digits(digits, reverse=True) #if not validate_classify(num1, num2, num3, num4): #num1, num2, num3, num4 = classify_digits(digits[:int(digits.shape[0] * 0.70), :], digits, reverse=True) # Save the output string of numbers output = str(num1) + str(num2) + str(num3) dir = num4 # If the direction is Left if dir == 10: output += "L" elif dir == 11: # if it is right output += "R" # Add the numbers to the list of directions on this sign numbers_on_sign.append(output) # return the numbers on the sign as a list and as a region return np.array(numbers_on_sign), region_output else: # Not a valid image, return error return "????", np.zeros((1, 1))
def gradient(plane, k=3): gx, gy = cv2.spatialGradient(plane, k, k) return np.concatenate([gx[..., None], gy[..., None]], axis=2).astype( np.float32) / 255.0
def find_goal_point(hsv_white_only, inner_corners): number_of_corners = len(inner_corners) average_filter_size = 19 img_average_intensity = make_circle_average_blurry(hsv_white_only, average_filter_size) ###################### # Define the inner_corners # if number_of_corners == 1: corner_0 = inner_corners[0] elif number_of_corners == 2: left_corner_id = np.argmin(inner_corners[:, 1]) right_corner_id = 1 - left_corner_id corner_0 = inner_corners[left_corner_id] corner_1 = inner_corners[right_corner_id] elif number_of_corners == 3: distances = np.array([ np.linalg.norm(inner_corners[0] - inner_corners[1]), np.linalg.norm(inner_corners[1] - inner_corners[2]), np.linalg.norm(inner_corners[2] - inner_corners[0]) ]) median = np.median(distances) median_id = np.where(distances == median)[0][0] print "median:", median print "median_id:", median_id if median_id == 0: relevant_corners = np.stack((inner_corners[0], inner_corners[1]), axis=0) elif median_id == 1: relevant_corners = np.stack((inner_corners[1], inner_corners[2]), axis=0) elif median_id == 2: relevant_corners = np.stack((inner_corners[2], inner_corners[0]), axis=0) else: print( "ERROR: In fn. detect_sub_pixecl_corners(); min_dist_id out of bounds" ) return None, None dist_0_1, dist_1_2, dist_2_0 = distances[0], distances[1], distances[2] # Pick the corner with the lowest y-coordinate left_corner_id = np.argmin(relevant_corners, axis=0)[1] right_corner_id = 1 - left_corner_id corner_0 = relevant_corners[left_corner_id] corner_1 = relevant_corners[right_corner_id] elif number_of_corners == 4: # For the first corner, chose the corner closest to the top # This will belong to the top cross-bar top_corner_id = np.argmin(inner_corners[:, 0]) # Find lowest x-index top_corner = inner_corners[top_corner_id] top_corner_stack = np.array([top_corner] * 3) rest_corners = np.delete(inner_corners, top_corner_id, 0) dist = np.linalg.norm(rest_corners - top_corner_stack, axis=1) # For the second corner, chose the corner closest to top corner top_corner_closest_id = np.argmin(dist) top_corner_closest = rest_corners[top_corner_closest_id] relevant_corners = np.stack((top_corner, top_corner_closest), axis=0) # Choose the corner with the lowest y-coordinate as the first corner left_corner_id = np.argmin(relevant_corners, axis=0)[1] right_corner_id = 1 - left_corner_id corner_0 = relevant_corners[left_corner_id] corner_1 = relevant_corners[right_corner_id] else: print("Invalid number of corners") return None, None ################################## # Define goal point and direction #' goal_point = None goal_direction = None # Calculate spatial gradient dy, dx = cv2.spatialGradient( hsv_white_only ) # Invert axis, since OpenCV operates with x=column, y=row if number_of_corners == 1: grad_0 = np.array( [dx[corner_0[0]][corner_0[1]], dy[corner_0[0]][corner_0[1]]]) grad_0_normalized = normalize_vector(grad_0) goal_point_offset = 5 # px-distance goal_point = np.int0(corner_0 + grad_0_normalized * goal_point_offset) goal_direction = grad_0_normalized else: grad_0 = np.array( [dx[corner_0[0]][corner_0[1]], dy[corner_0[0]][corner_0[1]]]) grad_1 = np.array( [dx[corner_1[0]][corner_1[1]], dy[corner_1[0]][corner_1[1]]]) grad_sum = grad_0 + grad_1 grad_sum_normalized = normalize_vector(grad_sum) cross_over_vector = corner_1 - corner_0 goal_point = np.int0(corner_0 + 0.5 * (cross_over_vector)) goal_value = img_average_intensity[goal_point[0]][goal_point[1]] if (number_of_corners == 2 or number_of_corners == 3) and goal_value < 200: print "The two inner_corners form the longitudinal bar" # wp # Choose to focus on the uppermost corner, # since it is assumed the orientation is approximately right longitudinal_corners = np.stack((corner_0, corner_1)) upper_corner_id = np.argmin(longitudinal_corners[:, 0]) lower_corner_id = 1 - upper_corner_id upper_corner = longitudinal_corners[upper_corner_id] lower_corner = longitudinal_corners[lower_corner_id] longitudinal_vector = upper_corner - lower_corner longitudinal_unit_vector = normalize_vector(longitudinal_vector) upper_corner_gradient = get_gradient_of_point(upper_corner, dx, dy) upper_corner_unit_gradient = normalize_vector(upper_corner_gradient) longitudinal_length = np.linalg.norm(longitudinal_vector) travese_length = longitudinal_length * REL_TRAV_LONG length_from_upper_corner_to_goal = travese_length / 2.0 arrow_length = 10 grad_start = upper_corner grad_end = np.int0(upper_corner + upper_corner_unit_gradient * arrow_length) angle = calc_angle_between_vectors(longitudinal_vector, upper_corner_unit_gradient) l_x, l_y = longitudinal_unit_vector[0], longitudinal_unit_vector[1] if angle > 0: # This means the gradient is pointing to the left dir_vector = np.array([-l_y, l_x]) else: # The gradient is pointing to the right (or is parallel) dir_vector = np.array([l_y, -l_x]) goal_point = np.int0(upper_corner + dir_vector * length_from_upper_corner_to_goal) goal_point = limit_point_to_be_inside_image(goal_point) print "Angle:", np.degrees(angle) hsv_drawn_vectors = hsv_white_only.copy() hsv_drawn_vectors = draw_arrow(hsv_drawn_vectors, grad_start, grad_end) hsv_drawn_vectors = draw_arrow(hsv_drawn_vectors, upper_corner, goal_point) hsv_save_image(hsv_drawn_vectors, "0_drawn_vectors", is_gray=True) goal_direction = longitudinal_unit_vector elif number_of_corners != 1: if grad_sum_normalized[0] < 0: cross_over_norm = np.array( [-cross_over_vector[1], cross_over_vector[0]]) else: cross_over_norm = np.array( [cross_over_vector[1], -cross_over_vector[0]]) goal_direction = normalize_vector(cross_over_norm) ################### # Draw the result # # direction_length = 20 # direction_point = np.int0(goal_point + dir_vector*direction_length) # img_grad = hsv_origin.copy() # img_grad = cv2.arrowedLine(img_grad, (goal_point[1], goal_point[0]), # (direction_point[1], direction_point[0]), # color = (255,0,0), thickness = 2, tipLength = 0.5) # hsv_save_image(img_grad, "5_gradient") # hsv_save_image(img_grad, str(img_id) +"_gradient") return goal_point, goal_direction