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
Beispiel #2
0
    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
        else:
            assert False, 'the dimension of the image is not correct'

        filtered_image = ndimage.filters.convolve(np_image, self.weights)
        return filtered_image
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_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_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_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 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
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_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 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
Beispiel #10
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
Beispiel #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
Beispiel #12
0
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
Beispiel #13
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