Ejemplo n.º 1
0
def safe_angle(input_angle, radian=False, warning=True, debug=True):
    '''
	make ensure the rotation is in [-180, 180] in degree

	parameters:
		input_angle:	an angle which is supposed to be in degree
		radian:			if True, the unit is replaced to radian instead of degree

	outputs:
		angle:			an angle in degree within (-180, 180]
	'''
    angle = copy.copy(input_angle)
    if debug:
        assert isscalar(angle), 'the input angle should be a scalar'

    if isnparray(angle): angle = angle[0]  # single numpy scalar value
    if radian:
        while angle > np.pi:
            angle -= np.pi
        while angle <= -np.pi:
            angle += np.pi
    else:
        while angle > 180:
            angle -= 360
        while angle <= -180:
            angle += 360

    return angle
Ejemplo n.º 2
0
def image_draw_mask(input_image,
                    input_image_mask,
                    transparency=0.3,
                    warning=True,
                    debug=True):
    '''
	draw a mask on top of an image with certain transparency

	parameters: 
		input_image:			a pil or numpy image
		input_image_mask:		a pil or numpy image
		transparency:			transparency factor

	outputs:
		masked_image:			uint8 numpy image
	'''
    np_image, _ = safe_image(input_image, warning=warning, debug=debug)
    np_image_mask, _ = safe_image(input_image_mask,
                                  warning=warning,
                                  debug=debug)
    if debug:
        assert isscalar(transparency), 'the transparency should be a scalar'
        assert np_image.shape == np_image_mask.shape, 'the shape of mask should be equal to the shape of input image'
    if isfloatimage(np_image): np_image = (np_image * 255.).astype('uint8')
    if isfloatimage(np_image_mask):
        np_image_mask = (np_image_mask * 255.).astype('uint8')

    pil_image, pil_image_mask = Image.fromarray(np_image), Image.fromarray(
        np_image_mask)
    masked_image = np.array(
        Image.blend(pil_image, pil_image_mask, alpha=transparency))
    return masked_image
Ejemplo n.º 3
0
def radian2degree(radian, debug=True):
    '''
    this function return radian given degree, difference from default math.degrees is that this function normalize the output in range [0, 360)
    '''
    if debug: assert isscalar(degree), 'input radian number is not correct'
    degree = math.degrees(radian)
    while degree < 0: degree += 360.0
    while degree >= 360.0: degree -= 360.0

    return degree
Ejemplo n.º 4
0
def degree2radian(degree, debug=True):
    '''
    this function return degree given radians, difference from default math.degrees is that this function normalize the output in range [0, 2*pi)
    '''
    if debug: assert isscalar(degree), 'input degree number is not correct'
    radian = math.radians(degree)
    while radian < 0: radian += 2*math.pi
    while radian >= 2*math.pi: radian -= 2*math.pi

    return radian
Ejemplo n.º 5
0
def safe_npdata(input_data, warning=True, debug=True):
    '''
	copy a list of data or a numpy data to the buffer for use

	parameters:
		input_data:		a list, a scalar or numpy data

	outputs:
		np_data:		a copy of numpy data
	'''
    if islist(input_data): np_data = np.array(input_data)
    elif isscalar(input_data): np_data = np.array(input_data).reshape((1, ))
    elif isnparray(input_data): np_data = input_data.copy()
    else: assert False, 'only list of data, scalar or numpy data are supported'

    return np_data
Ejemplo n.º 6
0
def generate_gaussian_heatmap(input_pts, image_size, std, warning=True, debug=True):
	'''
	generate a heatmap based on the input points array, create a 2-D gaussian with given std around each points provided
	the mask is generated by the occlusion from the point array: only occlusion with -1 will be masked out
	    0 -> invisible points without location
	    1 -> visible points with location
	    -1 -> visible points without location, masked

	parameters:
	    input_pts:          a list of 3 elements, a listoflist of 3 elements: e.g., [[1,2], [5,6], [0, 1]],
	                        a numpy array with shape or (3, N) or (3, )
	    image_size:         a tuple or list of numpy array with 2 elements, representing (height, width)
	    std:                the standard deviation used for gaussian distribution

	outputs:
	    masked_heatmap:         numpy float32 multichannel numpy array, (height, width, num_pts + 1)
	    mask_valid:             numpy float32 multichannel numpy array, (1, 1, num_pts + 1)
	    mask_visible:           numpy float32 multichannel numpy array, (1, 1, num_pts + 1)
	'''
	pts_array = safe_2dptsarray_occlusion(input_pts, warning=warning, debug=debug)
	if debug:
		assert isscalar(std), 'the standard deviation should be a scalar'
		assert isimsize(image_size), 'the image size is not correct'
	height, width = image_size[0], image_size[1]
	num_pts, threshold = pts_array.shape[1], 0.01
	heatmap = np.fromfunction( lambda y, x, pts_id : ((x - pts_array[0, pts_id])**2 \
	                                                + (y - pts_array[1, pts_id])**2) \
	                                                / -2.0 / std / std, (height, width, num_pts), dtype=int)
	heatmap = np.exp(heatmap)

	valid = np.logical_or(pts_array[2, :] == 0, pts_array[2, :] == 1)       # mask out invalid points with -1 in the third
	visible = pts_array[2, :] == 1                                          # mask out invalid and occuluded points
	mask_valid = np.ones((1, 1, num_pts+1), dtype='float32')
	mask_valid[0, 0, :num_pts] = valid                                      # never mask out the background channel
	mask_visible = np.ones((1, 1, num_pts+1), dtype='float32')
	mask_visible[0, 0, :num_pts] = visible                                  # never mask out the background channel

	# mask out the invalid channel
	heatmap[heatmap < threshold] = 0                                    # ceiling and flooring
	heatmap[heatmap >         1] = 1
	masked_heatmap = heatmap * mask_valid[:, :, :num_pts]               # (height, width, num_pts)

	background_label = 1 - np.amax(masked_heatmap, axis=2)              # (height, width), maximize along the channel axis
	background_label[background_label < 0] = 0                          # (height, width, 1)
	masked_heatmap = np.concatenate((masked_heatmap, np.expand_dims(background_label, axis=2)), axis=2).astype('float32')

	return masked_heatmap, mask_valid, mask_visible
Ejemplo n.º 7
0
def data_normalize(input_data,
                   method='max',
                   data_range=None,
                   sum=1,
                   warning=True,
                   debug=True):
    '''
	this function normalizes N-d data in different ways: 1) normalize the data from a range to [0, 1]; 2) normalize the data which sums to a value

	parameters:
		input_data:			a list or a numpy N-d data to normalize
		method:				max:	normalize the data from a range to [0, 1], when the range is not given, the max and min are obtained from the data
							sum:	normalize the data such that all elements are summed to a value, the default value is 1
		data_range:			None or 2-element tuple, list or array
		sum:				a scalar

	outputs:
		normalized_data:	a float32 numpy array with same shape as the input data
	'''
    np_data = safe_npdata(input_data, warning=warning,
                          debug=debug).astype('float32')
    if debug:
        assert isnparray(np_data), 'the input data is not a numpy data'
        assert method in ['max',
                          'sum'], 'the method for normalization is not correct'

    if method == 'max':
        if data_range is None:
            max_value, min_value = np.max(np_data), np.min(np_data)
        else:
            if debug: assert isrange(data_range), 'data range is not correct'
            max_value, min_value = data_range[1], data_range[0]
    elif method == 'sum':
        if debug: assert isscalar(sum), 'the sum is not correct'
        max_value, min_value = np.sum(np_data) / sum, 0

    normalized_data = (np_data - min_value) / (max_value - min_value
                                               )  # normalization

    return normalized_data
Ejemplo n.º 8
0
def nparray_resize(input_nparray, resize_factor=None, target_size=None, interp='bicubic', warning=True, debug=True):
    '''
    resize the numpy array given a resize factor (e.g., 0.25), or given a target size (height, width)
    e.g., the numpy array has 600 x 800:
        1. given a resize factor of 0.25 -> results in an image with 150 x 200
        2. given a target size of (300, 400) -> results in an image with 300 x 400
    note that:
        resize_factor and target_size cannot exist at the same time

    parameters:
        input_nparray:      a numpy array
        resize_factor:      a scalar
        target_size:        a list of tuple or numpy array with 2 elements, representing height and width
        interp:             interpolation methods: bicubic or bilinear

    outputs:
        resized_nparray:    a numpy array
    ''' 
    np_array = safe_npdata(input_nparray, warning=warning, debug=debug)
    if debug:
        assert interp in ['bicubic', 'bilinear'], 'the interpolation method is not correct'
        assert (resize_factor is not None and target_size is None) or (resize_factor is None and target_size is not None), 'resize_factor and target_size cannot co-exist'

    if target_size is not None:
        if debug: assert isimsize(target_size), 'the input target size is not correct'
        target_width, target_height = int(round(target_size[1])), int(round(target_size[0]))
    elif resize_factor is not None:
        if debug: assert isscalar(resize_factor), 'the resize factor is not a scalar'
        height, width = np_array.shape[:2]
        target_width, target_height = int(round(resize_factor * width)), int(round(resize_factor * height))
    else: assert False, 'the target_size and resize_factor do not exist'

    if interp == 'bicubic':
        resized_nparray = cv2.resize(np_array, (target_width, target_height), interpolation = cv2.INTER_CUBIC)
    elif interp == 'bilinear':
        resized_nparray = cv2.resize(np_array, (target_width, target_height), interpolation = cv2.INTER_LINEAR)
    else: assert False, 'interpolation is wrong'

    return resized_nparray
Ejemplo n.º 9
0
def image_rotate(input_image, input_angle, warning=True, debug=True):
    '''
	rotate the image given an angle in degree (e.g., 90). The rotation is counter-clockwise
	
	parameters:
		input_image:		an pil or numpy image
		input_angle:		a scalar, counterclockwise rotation in degree

	outputs:
		rotated_image:		a numpy uint8 image
	'''
    if debug: assert isscalar(input_angle), 'the input angle is not a scalar'
    rotation_angle = safe_angle(input_angle, warning=warning,
                                debug=True)  # ensure to be in [-180, 180]
    np_image, _ = safe_image(input_image, warning=warning, debug=debug)
    if input_angle == 0: return np_image

    if isfloatimage(np_image): np_image = (np_image * 255.).astype('uint8')
    pil_image = Image.fromarray(np_image)
    if rotation_angle != 0:
        pil_image = pil_image.rotate(rotation_angle, expand=True)
    rotated_image = np.array(pil_image).astype('uint8')

    return rotated_image
Ejemplo n.º 10
0
def image_concatenate(input_image,
                      target_size=[1600, 2560],
                      grid_size=None,
                      edge_factor=0.99,
                      fill_value=0,
                      warning=True,
                      debug=True):
    '''
	concatenate a list of images automatically

	parameters:	
		input_image:			NHWC numpy image, uint8 or float32
		target_size:			a tuple or list or numpy array with 2 elements, for [H, W]
		grid_size:				a tuple or list or numpy array with 2 elements, for [num_rows, num_cols] 
		edge_factor:			the margin between images after concatenation, bigger, the edge is smaller, [0, 1]
		fill_value:				float between 0-1 to fill the gap

	outputs:
		image_merged: 			CHW uint8 numpy image with size of target_size
	'''
    np_image, _ = safe_batch_image(input_image, warning=warning, debug=debug)
    if debug:
        assert isimsize(target_size), 'the input image size is not correct'
        if grid_size is not None:
            assert isimsize(grid_size), 'the input grid size is not correct'
        assert isscalar(
            edge_factor
        ) and edge_factor <= 1 and edge_factor >= 0, 'the edge factor is not correct'

    num_images = np_image.shape[0]
    if grid_size is None:
        num_rows = int(np.sqrt(num_images))
        num_cols = int(np.ceil(num_images * 1.0 / num_rows))
    else:
        num_rows, num_cols = np.ceil(grid_size[0]), np.ceil(grid_size[1])

    window_height, window_width = target_size[0], target_size[1]
    grid_height = int(window_height / num_rows)
    grid_width = int(window_width / num_cols)
    im_height = int(grid_height * edge_factor)
    im_width = int(grid_width * edge_factor)
    im_channel = np_image.shape[-1]

    # concatenate
    image_merged = np.zeros((window_height, window_width, im_channel),
                            dtype='uint8')
    image_merged.fill(fill_value)
    for image_index in range(num_images):
        image_tmp = np_image[image_index, :, :, :]
        image_tmp = image_resize(image_tmp,
                                 target_size=(im_height, im_width),
                                 warning=warning,
                                 debug=debug)

        rows_index = int(np.ceil((image_index + 1.0) / num_cols))  # 1-indexed
        cols_index = int(image_index + 1 -
                         (rows_index - 1) * num_cols)  # 1-indexed
        rows_start = int((rows_index - 1) * grid_height)  # 0-indexed
        rows_end = int(rows_start + im_height)  # 0-indexed
        cols_start = int((cols_index - 1) * grid_width)  # 0-indexed
        cols_end = int(cols_start + im_width)  # 0-indexed
        image_merged[rows_start:rows_end,
                     cols_start:cols_end, :] = image_tmp.reshape(
                         (im_height, im_width, im_channel))

    return image_merged
Ejemplo n.º 11
0
def image_resize(input_image,
                 resize_factor=None,
                 target_size=None,
                 interp='bicubic',
                 warning=True,
                 debug=True):
    '''
	resize the image given a resize factor (e.g., 0.25), or given a target size (height, width)
	e.g., the input image has 600 x 800:
		1. given a resize factor of 0.25 -> results in an image with 150 x 200
		2. given a target size of (300, 400) -> results in an image with 300 x 400
	note that:
		resize_factor and target_size cannot exist at the same time

	parameters:
		input_image:		an pil or numpy image
		resize_factor:		a scalar
		target_size:		a list of tuple or numpy array with 2 elements, representing height and width
		interp:				interpolation methods: bicubic or bilinear

	outputs:
		resized_image:		a numpy uint8 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 interp in ['bicubic', 'bilinear'
                          ], 'the interpolation method is not correct'
        assert (resize_factor is not None and target_size is None) or (
            resize_factor is None and target_size
            is not None), 'resize_factor and target_size cannot co-exist'

    if target_size is not None:
        if debug:
            assert isimsize(
                target_size), 'the input target size is not correct'
        target_width, target_height = int(round(target_size[1])), int(
            round(target_size[0]))
        if target_width == np_image.shape[
                1] and target_height == np_image.shape[0]:
            return np_image
    elif resize_factor is not None:
        if debug:
            assert isscalar(
                resize_factor
            ) and resize_factor > 0, 'the resize factor is not a scalar'
        if resize_factor == 1: return np_image  # no resizing
        height, width = np_image.shape[:2]
        target_width, target_height = int(round(resize_factor * width)), int(
            round(resize_factor * height))
    else:
        assert False, 'the target_size and resize_factor do not exist'

    if interp == 'bicubic':
        resized_image = cv2.resize(np_image, (target_width, target_height),
                                   interpolation=cv2.INTER_CUBIC)
    elif interp == 'bilinear':
        resized_image = cv2.resize(np_image, (target_width, target_height),
                                   interpolation=cv2.INTER_LINEAR)
    else:
        assert False, 'interpolation is wrong'

    return resized_image
Ejemplo n.º 12
0
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_dimension(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