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')