def gray2rgb(input_image, with_color=True, cmap='jet', warning=True, debug=True): ''' convert a grayscale image (1-channel) to a rgb image parameters: input_image: an pil or numpy image with_color: add false colormap output: rgb_image: an uint8 rgb numpy image ''' np_image, _ = safe_image(input_image, warning=warning, debug=debug) if isfloatimage(np_image): np_image = (np_image * 255.).astype('uint8') if debug: assert isgrayimage_dimension( np_image), 'the input numpy image is not correct: {}'.format( np_image.shape) assert isuintimage( np_image ), 'the input numpy image should be uint8 image in order to use opencv' if with_color: if cmap == 'jet': rgb_image = cv2.applyColorMap(np_image, cv2.COLORMAP_JET) else: assert False, 'cmap %s is not supported' % cmap else: rgb_image = cv2.cvtColor(np_image, cv2.COLOR_GRAY2RGB) return rgb_image
def image_hist_equalization_hsv(input_image, warning=True, debug=True): ''' do histogram equalization for an image: could be a color image or gray image the color space used for histogram equalization is HSV parameters: input_image: a pil or numpy image outputs: equalized_image: an uint8 numpy image (rgb or gray) ''' np_image, _ = safe_image(input_image, warning=warning, debug=debug) if isuintimage(np_image): np_image = np_image.astype('float32') / 255. if debug: assert isfloatimage( np_image), 'the input image should be a float image' if iscolorimage_dimension(np_image): hsv_image = rgb2hsv(np_image, warning=warning, debug=debug) input_data = hsv_image[:, :, 2] # extract the value channel equalized_hsv_image = ( hist_equalization(input_data, num_bins=256, debug=debug) * 255.).astype('uint8') hsv_image[:, :, 2] = equalized_hsv_image equalized_image = hsv2rgb(hsv_image, warning=warning, debug=debug) elif isgrayimage_dimension(np_image): equalized_image = ( hist_equalization(np_image, num_bins=256, debug=debug) * 255.).astype('uint8') else: assert False, 'the input image is neither a color image or a grayscale image' return equalized_image
def image_clahe(input_image, clip_limit=2.0, grid_size=8, warning=True, debug=True): ''' do contrast limited adative histogram equalization for an image: could be a color image or gray image the color space used for histogram equalization is LAB parameters: input_image: a pil or numpy image outputs: clahe_image: an uint8 numpy image (rgb or gray) ''' np_image, _ = safe_image(input_image, warning=warning, debug=debug) if isfloatimage(np_image): np_image = (np_image.astype('float32') * 255.).astype('uint8') if debug: assert isuintimage(np_image), 'the input image should be a uint8 image' clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=(grid_size, grid_size)) if iscolorimage_dimension(np_image): lab_image = rgb2lab(np_image, warning=warning, debug=debug) input_data = lab_image[:, :, 0] # extract the value channel clahe_lab_image = clahe.apply(input_data) lab_image[:, :, 0] = clahe_lab_image clahe_image = lab2rgb(lab_image, warning=warning, debug=debug) elif isgrayimage_dimension(np_image): clahe_image = clahe.apply(np_image) else: assert False, 'the input image is neither a color image or a grayscale image' return clahe_image
def image_pad_around(input_image, pad_rect, pad_value=0, warning=True, debug=True): ''' this function is to pad given value to an image in provided region, all images in this function are floating images parameters: input_image: an pil or numpy image pad_rect: a list of 4 non-negative integers, describing how many pixels to pad. The order is [left, top, right, bottom] pad_value: an intger between [0, 255] outputs: img_padded: an uint8 numpy image with padding ''' np_image, _ = safe_image(input_image, warning=warning, debug=debug) if isfloatimage(np_image): np_image = (np_image * 255.).astype('uint8') if len(np_image.shape) == 2: np_image = np.expand_dims(np_image, axis=2) # extend the third channel if the image is grayscale if debug: assert isuintimage(np_image), 'the input image is not an uint8 image' assert isinteger(pad_value) and pad_value >= 0 and pad_value <= 255, 'the pad value should be an integer within [0, 255]' assert islistofnonnegativeinteger(pad_rect) and len(pad_rect) == 4, 'the input pad rect is not a list of 4 non-negative integers' im_height, im_width, im_channel = np_image.shape[0], np_image.shape[1], np_image.shape[2] # calculate the padded size of image pad_left, pad_top, pad_right, pad_bottom = pad_rect[0], pad_rect[1], pad_rect[2], pad_rect[3] new_height = im_height + pad_top + pad_bottom new_width = im_width + pad_left + pad_right # padding img_padded = np.zeros([new_height, new_width, im_channel]).astype('uint8') img_padded.fill(pad_value) img_padded[pad_top : new_height - pad_bottom, pad_left : new_width - pad_right, :] = np_image if img_padded.shape[2] == 1: img_padded = img_padded[:, :, 0] return img_padded
def convolve(self, input_image): ''' convolve the kernel with the input image, whatever the input image format is. If the input image is a color image, the filter is expanded to a 3D shape parameters: input_image: an pil or numpy, gray or color image outputs: filtered_image: a float32 numpy image, shape is same as before ''' np_image, _ = safe_image(input_image, warning=self.warning, debug=self.debug) if isuintimage(np_image): np_image = np_image.astype('float32') / 255. if self.debug: assert isfloatimage( np_image), 'the input image should be a float image' self.weights is not None, 'the kernel is not defined yet' if iscolorimage_dimension(np_image): self.weights = self.expand_3d( ) # expand the filter to 3D for color image elif isgrayimage_dimension(np_image): np_image = np_image.reshape( np_image.shape[0], np_image.shape[1]) # squeeze the image dimension to 2 filtered_image = ndimage.filters.convolve(np_image, self.weights) return filtered_image
def image_find_peaks(input_image, percent_threshold=0.5, warning=True, debug=True): ''' this function find all strict local peaks and a strict global peak from a grayscale image the strict local maximum means that the pixel value must be larger than all nearby pixel values parameters: input_image: a pil or numpy grayscale image percent_threshold: determine to what pixel value to be smoothed out. e.g., when 0.4, all pixel values less than 0.4 * np.max(input_image) are smoothed out to be 0 outputs: peak_array: a numpy float32 array, 3 x num_peaks, (x, y, score) peak_global: a numpy float32 array, 3 x 1: (x, y, score) ''' np_image, _ = safe_image_like(input_image, warning=warning, debug=debug) if isuintimage(np_image): np_image = np_image.astype('float32') / 255. if debug: assert isgrayimage(np_image) and isfloatimage(np_image), 'the input image is not a grayscale and float image' assert isscalar(percent_threshold) and percent_threshold >= 0 and percent_threshold <= 1, 'the percent_threshold is not correct' max_value = np.max(np_image) np_image[np_image < percent_threshold * max_value] = 0.0 height, width = np_image.shape[0], np_image.shape[1] npimage_center, npimage_top, npimage_bottom, npimage_left, npimage_right = np.zeros([height + 2, width + 2]), np.zeros([height + 2, width + 2]), np.zeros([height + 2, width + 2]), np.zeros([height + 2, width + 2]), np.zeros([height + 2, width + 2]) # shift in different directions to find local peak, only works for convex blob npimage_center[1:-1, 1:-1] = np_image npimage_left[1:-1, 0:-2] = np_image npimage_right[1:-1, 2:] = np_image npimage_top[0:-2, 1:-1] = np_image npimage_bottom[2:, 1:-1] = np_image # compute pixels larger than its shifted version of heatmap right_bool = npimage_center > npimage_right left_bool = npimage_center > npimage_left bottom_bool = npimage_center > npimage_bottom top_bool = npimage_center > npimage_top # the strict local maximum must be bigger than all nearby pixel values peakMap = np.logical_and(np.logical_and(np.logical_and(right_bool, left_bool), top_bool), bottom_bool) peakMap = peakMap[1:-1, 1:-1] peak_location_tuple = np.nonzero(peakMap) # find true num_peaks = len(peak_location_tuple[0]) if num_peaks == 0: if warning: print('No single local peak found') return np.zeros((3, 0), dtype='float32'), np.zeros((3, 0), dtype='float32') # convert to a numpy array format peak_array = np.zeros((3, num_peaks), dtype='float32') peak_array[0, :], peak_array[1, :] = peak_location_tuple[1], peak_location_tuple[0] for peak_index in range(num_peaks): peak_array[2, peak_index] = np_image[int(peak_array[1, peak_index]), int(peak_array[0, peak_index])] # find the global peak from all local peaks global_peak_index = np.argmax(peak_array[2, :]) peak_global = peak_array[:, global_peak_index].reshape((3, 1)) return peak_array, peak_global
def hsv2rgb(input_image, warning=True, debug=True): ''' convert a hsv image to a rgb image using opencv package parameters: input_image: an pil or numpy image output: rgb_img: an uint8 rgb numpy image ''' np_image, _ = safe_image(input_image, warning=warning, debug=debug) if isfloatimage(np_image): np_image = (np_image * 255.).astype('uint8') if debug: assert iscolorimage(np_image), 'the input image should be a rgb image' assert isuintimage(np_image), 'the input numpy image should be uint8 image in order to use opencv' rgb_img = cv2.cvtColor(np_image, cv2.COLOR_HSV2RGB) return rgb_img
def rgb2hsv_v2(input_image, warning=True, debug=True): ''' convert a rgb image to a hsv image, using PIL package, not compatible with opencv package parameters: input_image: an pil or numpy image output: hsv_image: an uint8 hsv numpy image ''' np_image, _ = safe_image(input_image, warning=warning, debug=debug) if isfloatimage(np_image): np_image = (np_image * 255.).astype('uint8') if debug: assert iscolorimage(np_image), 'the input image should be a rgb image' assert isuintimage(np_image), 'the input numpy image should be uint8 image in order to use PIL' pil_rgb_img = Image.fromarray(np_image) pil_hsv_img = pil_rgb_img.convert('HSV') hsv_img = np.array(pil_hsv_img) return hsv_img
def image_crop_center(input_image, center_rect, pad_value=0, warning=True, debug=True): ''' crop the image around a specific center with padded value around the empty area when the crop width/height are even, the cropped image has 1 additional pixel towards left/up parameters: center_rect: a list contains [center_x, center_y, (crop_width, crop_height)] pad_value: scalar within [0, 255] outputs: img_cropped: an uint8 numpy image crop_bbox: numpy array with shape of (1, 4), user-attempted cropping bbox, might out of boundary crop_bbox_clipped: numpy array with shape of (1, 4), clipped bbox within the boundary ''' np_image, _ = safe_image(input_image, warning=warning, debug=debug) if isfloatimage(np_image): np_image = (np_image * 255.).astype('uint8') if len(np_image.shape) == 2: np_image = np.expand_dims(np_image, axis=2) # extend the third channel if the image is grayscale # center_rect and pad_value are checked in get_crop_bbox and pad_around functions if debug: assert isuintimage(np_image), 'the input image is not an uint8 image' im_height, im_width = np_image.shape[0], np_image.shape[1] # calculate crop rectangles crop_bbox = get_center_crop_bbox(center_rect, im_width, im_height, debug=debug) crop_bbox_clipped = clip_bboxes_TLWH(crop_bbox, im_width, im_height, debug=debug) x1, y1, x2, y2 = crop_bbox_clipped[0, 0], crop_bbox_clipped[0, 1], crop_bbox_clipped[0, 0] + crop_bbox_clipped[0, 2], crop_bbox_clipped[0, 1] + crop_bbox_clipped[0, 3] img_cropped = np_image[y1 : y2, x1 : x2, :] # if original image is not enough to cover the crop area, we pad value around outside after cropping xmin, ymin, xmax, ymax = crop_bbox[0, 0], crop_bbox[0, 1], crop_bbox[0, 0] + crop_bbox[0, 2], crop_bbox[0, 1] + crop_bbox[0, 3] if (xmin < 0 or ymin < 0 or xmax > im_width or ymax > im_height): pad_left = max(0 - xmin, 0) pad_top = max(0 - ymin, 0) pad_right = max(xmax - im_width, 0) pad_bottom = max(ymax - im_height, 0) pad_rect = [pad_left, pad_top, pad_right, pad_bottom] img_cropped = image_pad_around(img_cropped, pad_rect=pad_rect, pad_value=pad_value, debug=debug) if len(img_cropped.shape) == 3 and img_cropped.shape[2] == 1: img_cropped = img_cropped[:, :, 0] return img_cropped, crop_bbox, crop_bbox_clipped
def rgb2hsv(input_image, warning=True, debug=True): ''' convert a rgb image to a hsv image using opencv package parameters: input_image: an pil or numpy image output: hsv_image: an uint8 hsv numpy image ''' np_image, _ = safe_image(input_image, warning=warning, debug=debug) if isfloatimage(np_image): np_image = (np_image * 255.).astype('uint8') if debug: assert iscolorimage_dimension( np_image), 'the input image should be a rgb image' assert isuintimage( np_image ), 'the input numpy image should be uint8 image in order to use opencv' hsv_img = cv2.cvtColor(np_image, cv2.COLOR_RGB2HSV) return hsv_img
def rgb2gray(input_image, warning=True, debug=True): ''' convert a color image to a grayscale image (1-channel) parameters: input_image: an pil or numpy image output: gray_image: an uint8 HW gray numpy image ''' np_image, _ = safe_image(input_image, warning=warning, debug=debug) if isfloatimage(np_image): np_image = (np_image * 255.).astype('uint8') if debug: assert iscolorimage_dimension( np_image), 'the input numpy image is not correct: {}'.format( np_image.shape) assert isuintimage( np_image ), 'the input numpy image should be uint8 image in order to use opencv' gray_image = cv2.cvtColor(np_image, cv2.COLOR_RGB2GRAY) return gray_image