def image_normalize(input_image, warning=True, debug=True): ''' normalize an image to an uint8 with range of [0, 255] note that: the input might not be an image because the value range might be arbitrary parameters: input_image: pil image or image-like array, color or gray, float or uint outputs: np_image: numpy uint8 image, normalized to [0, 255] ''' np_image, isnan = safe_image_like(input_image, warning=warning, debug=debug) if isuintnparray(np_image): np_image = np_image.astype('float32') / 255. else: assert isfloatnparray(np_image), 'the input image-like array should be either an uint8 or float32 array' min_val = np.min(np_image) max_val = np.max(np_image) if isnan: np_image.fill(0) elif min_val == max_val: # all same if warning: print('the input image has the same value over all the pixels') np_image.fill(0) else: # normal case np_image -= min_val np_image = ((np_image / (max_val - min_val)) * 255.).astype('uint8') if debug: assert np.min(np_image) == 0 and np.max(np_image) == 255, 'the value range is not right [%f, %f]' % (np.min(np_image), np.max(np_image)) return np_image.astype('uint8')
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 test_safe_image_like(): image_path = '../lena.jpg' print('test pil image in normal case') img_pil = Image.open(image_path) img_bak = np.array(img_pil).copy() copy_image, isnan = safe_image_like(img_pil) assert CHECK_EQ_NUMPY( copy_image, np.asarray(img_pil)), 'the original image should be equal to the copy' copy_image += 1 assert not CHECK_EQ_NUMPY( copy_image, np.asarray(img_pil)), 'the original image should be equal to the copy' assert CHECK_EQ_NUMPY(img_bak, np.asarray( img_pil)), 'the original image should be equal to the backup version' assert not isnan print('test numpy image in normal case') img_numpy = np.asarray(img_pil) # read only img_bak = img_numpy.copy() copy_image, isnan = safe_image_like(img_numpy) assert CHECK_EQ_NUMPY( copy_image, img_numpy), 'the original image should be equal to the copy' copy_image += 1 assert not CHECK_EQ_NUMPY( copy_image, img_numpy), 'the original image should be equal to the copy' assert CHECK_EQ_NUMPY( img_bak, img_numpy), 'the original image should be equal to the backup version' assert not isnan print('test numpy image with arbitrary value') img_numpy = np.random.rand(100, 100) img_numpy += np.random.rand(100, 100) img_numpy += np.random.rand(100, 100) img_numpy += np.random.rand(100, 100) img_bak = img_numpy.copy() copy_image, isnan = safe_image_like(img_numpy) assert CHECK_EQ_NUMPY( copy_image, img_numpy), 'the original image should be equal to the copy' copy_image += 1 assert not CHECK_EQ_NUMPY( copy_image, img_numpy), 'the original image should be equal to the copy' assert CHECK_EQ_NUMPY( img_bak, img_numpy), 'the original image should be equal to the backup version' assert not isnan print('test numpy image with nan') img_numpy = np.random.rand(100, 100) img_numpy[0, 0] = float('nan') img_bak = img_numpy.copy() copy_image, isnan = safe_image_like(img_numpy) assert isnan print('\n\nDONE! SUCCESSFUL!!\n')