def sobel_filter(image): kernel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) kernel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]]) dst_x = img_convolve(image, kernel_x) dst_y = img_convolve(image, kernel_y) dst = np.sqrt((np.square(dst_x)) + (np.square(dst_y))).astype(np.uint8) degree = np.arctan2(dst_y, dst_x) return dst, degree
def sobel_filter(image): kernel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) kernel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]]) dst_x = np.abs(img_convolve(image, kernel_x)) dst_y = np.abs(img_convolve(image, kernel_y)) # modify the pix within [0, 255] dst_x = dst_x * 255 / np.max(dst_x) dst_y = dst_y * 255 / np.max(dst_y) dst_xy = np.sqrt((np.square(dst_x)) + (np.square(dst_y))) dst_xy = dst_xy * 255 / np.max(dst_xy) dst = dst_xy.astype(np.uint8) theta = np.arctan2(dst_y, dst_x) return dst, theta
def canny(image, threshold_low=15, threshold_high=30, weak=128, strong=255): image_row, image_col = image.shape[0], image.shape[1] # gaussian_filter gaussian_out = img_convolve(image, gen_gaussian_kernel(9, sigma=1.4)) # get the gradient and degree by sobel_filter sobel_grad, sobel_theta = sobel_filter(gaussian_out) gradient_direction = np.rad2deg(sobel_theta) gradient_direction += PI dst = np.zeros((image_row, image_col)) """ Non-maximum suppression. If the edge strength of the current pixel is the largest compared to the other pixels in the mask with the same direction, the value will be preserved. Otherwise, the value will be suppressed. """ for row in range(1, image_row - 1): for col in range(1, image_col - 1): direction = gradient_direction[row, col] if ( 0 <= direction < 22.5 or 15 * PI / 8 <= direction <= 2 * PI or 7 * PI / 8 <= direction <= 9 * PI / 8 ): W = sobel_grad[row, col - 1] E = sobel_grad[row, col + 1] if sobel_grad[row, col] >= W and sobel_grad[row, col] >= E: dst[row, col] = sobel_grad[row, col] elif (PI / 8 <= direction < 3 * PI / 8) or ( 9 * PI / 8 <= direction < 11 * PI / 8 ): SW = sobel_grad[row + 1, col - 1] NE = sobel_grad[row - 1, col + 1] if sobel_grad[row, col] >= SW and sobel_grad[row, col] >= NE: dst[row, col] = sobel_grad[row, col] elif (3 * PI / 8 <= direction < 5 * PI / 8) or ( 11 * PI / 8 <= direction < 13 * PI / 8 ): N = sobel_grad[row - 1, col] S = sobel_grad[row + 1, col] if sobel_grad[row, col] >= N and sobel_grad[row, col] >= S: dst[row, col] = sobel_grad[row, col] elif (5 * PI / 8 <= direction < 7 * PI / 8) or ( 13 * PI / 8 <= direction < 15 * PI / 8 ): NW = sobel_grad[row - 1, col - 1] SE = sobel_grad[row + 1, col + 1] if sobel_grad[row, col] >= NW and sobel_grad[row, col] >= SE: dst[row, col] = sobel_grad[row, col] """ High-Low threshold detection. If an edge pixel’s gradient value is higher than the high threshold value, it is marked as a strong edge pixel. If an edge pixel’s gradient value is smaller than the high threshold value and larger than the low threshold value, it is marked as a weak edge pixel. If an edge pixel's value is smaller than the low threshold value, it will be suppressed. """ if dst[row, col] >= threshold_high: dst[row, col] = strong elif dst[row, col] <= threshold_low: dst[row, col] = 0 else: dst[row, col] = weak """ Edge tracking. Usually a weak edge pixel caused from true edges will be connected to a strong edge pixel while noise responses are unconnected. As long as there is one strong edge pixel that is involved in its 8-connected neighborhood, that weak edge point can be identified as one that should be preserved. """ for row in range(1, image_row): for col in range(1, image_col): if dst[row, col] == weak: if 255 in ( dst[row, col + 1], dst[row, col - 1], dst[row - 1, col], dst[row + 1, col], dst[row - 1, col - 1], dst[row + 1, col - 1], dst[row - 1, col + 1], dst[row + 1, col + 1], ): dst[row, col] = strong else: dst[row, col] = 0 return dst
def test_convolve_filter(): # laplace diagonals Laplace = array([[0.25, 0.5, 0.25], [0.5, -3, 0.5], [0.25, 0.5, 0.25]]) res = conv.img_convolve(gray, Laplace).astype(uint8) assert res.any()
def hog_feature(image, gamma=0.4, cell_size=6, bin_size=18, block_size=4): rows, cols = image.shape[0], image.shape[1] """ Gamma normalization, however, the author point out this step can be omitted in HOG descriptor computation. """ norm = (image + 0.5) / 255 norm = np.power(norm, 1 / gamma) img_norm = 255 * norm - 0.5 # Get gradient and angle, the kernel below perform better than others(3x3 Sobel). kernel_x = np.array([[-1, 0, 1]]) kernel_y = np.array([[-1], [0], [1]]) dst_x = img_convolve(img_norm, kernel_x) dst_y = img_convolve(img_norm, kernel_y) dst_xy = np.sqrt((np.square(dst_x)) + (np.square(dst_y))) dst_xy = dst_xy * 255 / np.max(dst_xy) gradient_magnitude_global = dst_xy # Get the angles and convert them in range (0, 360) theta = np.arctan2(dst_y, dst_x) gradient_angle_global = np.rad2deg(theta) # range(-180, 180) gradient_angle_global = np.where(gradient_angle_global < 0, gradient_angle_global + 360, gradient_angle_global) # range(0, 360) """ Orientation binning, The second step of calculation is creating the cell histograms. Each pixel within the cell casts a weighted vote for an orientation-based histogram channel based on the values found in the gradient computation. In tests, the gradient magnitude itself generally produces the best results. """ angle_unit = 360 / bin_size cell_gradient_mtx = np.zeros( (rows // cell_size, cols // cell_size, bin_size)) for i in range(cell_gradient_mtx.shape[0]): for j in range(cell_gradient_mtx.shape[1]): pixes_grad_per_cell = gradient_magnitude_global[i * cell_size:(i + 1) * cell_size, j * cell_size:(j + 1) * cell_size] pixes_angle_per_cell = gradient_angle_global[i * cell_size:(i + 1) * cell_size, j * cell_size:(j + 1) * cell_size] orientation_centers = [0] * bin_size for cell_i in range(pixes_grad_per_cell.shape[0]): for cell_j in range(pixes_grad_per_cell.shape[1]): gradient_strength = pixes_grad_per_cell[cell_i][cell_j] gradient_angle = pixes_angle_per_cell[cell_i][cell_j] bin_index = int(gradient_angle / angle_unit) if gradient_angle == 360: bin_index = 0 orientation_centers[bin_index] += gradient_strength cell_gradient_mtx[i][j] = orientation_centers """ Descriptor blocks. Grouping cells into larger spatial blocks and contrast normalizing each block separately. The final descriptor is then the vector of all components of the normalized cell responses from all of the blocks in the detection window. """ hog_descriptor_mtx = [] out_rows, out_cols = cell_gradient_mtx.shape[ 1] - block_size + 1, cell_gradient_mtx.shape[0] - block_size + 1 for i in range(0, out_rows): for j in range(0, out_cols): block_vector = np.ravel(cell_gradient_mtx[i:i + block_size, j:j + block_size, :]) # Block L2 normalization eps = 1e-5 block_vector = block_vector / np.sqrt( np.sum(block_vector**2) + eps**2) hog_descriptor_mtx.append(block_vector) # showing hog hog_dst_image = np.zeros([rows, cols]) cell_gradient = cell_gradient_mtx cell_width = cell_size // 2 max_mag = np.array(cell_gradient).max() for x in range(cell_gradient.shape[0]): for y in range(cell_gradient.shape[1]): cell_grad = cell_gradient[x][y] cell_grad /= max_mag angle = 0 angle_gap = angle_unit for magnitude in cell_grad: angle_radian = math.radians(angle) x1 = int(x * cell_size + magnitude * cell_width * math.cos(angle_radian)) y1 = int(y * cell_size + magnitude * cell_width * math.sin(angle_radian)) x2 = int(x * cell_size - magnitude * cell_width * math.cos(angle_radian)) y2 = int(y * cell_size - magnitude * cell_width * math.sin(angle_radian)) strength = int(255 * math.sqrt(magnitude)) cv2.line(hog_dst_image, (y1, x1), (y2, x2), strength) angle += angle_gap return hog_descriptor_mtx, hog_dst_image