def apply_kernel(image, kernel):
    """ Performs convolution between the given image and kernel """
    if utils.is_color(image):
        result_b = convolve2d(image[:, :, 0],
                              kernel,
                              mode='same',
                              fillvalue=np.median(image[:, :, 0]))
        result_g = convolve2d(image[:, :, 1],
                              kernel,
                              mode='same',
                              fillvalue=np.median(image[:, :, 1]))
        result_r = convolve2d(image[:, :, 2],
                              kernel,
                              mode='same',
                              fillvalue=np.median(image[:, :, 2]))
        channels_list = []

        # Trim values lower than 0 or higher than 255 and convert to uint8 for openCV compatibility
        for channel in 'bgr':
            underflow_mask = locals()['result_' + channel] < 0
            result_temp = np.where(underflow_mask, 0,
                                   locals()['result_' + channel])
            result_temp = np.where(result_temp > 255, 255, result_temp)
            result_temp = result_temp.astype(np.uint8)
            channels_list.append(result_temp)

        filtered_image = utils.merge_channels(channels_list)
    else:
        # Trim values lower than 0 or higher than 255 and convert to uint8 for openCV compatibility
        filtered_image = convolve2d(image, kernel, mode='same')
        filtered_image = np.where(filtered_image < 0, 0, filtered_image)
        filtered_image = np.where(filtered_image > 255, 255, filtered_image)
        filtered_image = filtered_image.astype(np.uint8)

    return filtered_image
def lines_shuffle(channels, state):
    """ Shuffles the lines of an image (The pixels on each line are left unchanged) """
    # Shuffle the lines in each channel, with the same permutation
    for channel in channels:
        np.random.shuffle(channel)
        # Reset the state so that next shuffle is same permutation
        np.random.set_state(state)

    shuffled_image = utils.merge_channels(channels)

    return shuffled_image
def columns_shuffle(channels, state):
    """ Shuffles the columns of an image (The pixels on each column are left unchanged) """
    # Shuffle the columns in each channel, with the same permutation
    for channel in channels:
        transpose = np.transpose(channel)
        np.random.shuffle(transpose)
        channel = np.transpose(transpose)
        # Reset the state so that next shuffle is same permutation
        np.random.set_state(state)

    shuffled_image = utils.merge_channels(channels)

    return shuffled_image
def pixels_shuffle(channels, state):
    """ Shuffles the pixels of an image """
    # Shuffle the pixels in each channel, with the same permutation
    for channel in channels:
        pixel_list = np.ravel(
            channel)  # Flattens the channel matrix to an array
        np.random.shuffle(pixel_list)
        channel = np.reshape(pixel_list, channel.shape)
        # Reset the state so that next shuffle is same permutation
        np.random.set_state(state)

    shuffled_image = utils.merge_channels(channels)

    return shuffled_image
def split_channels(image, extra_inputs, parameters):
    """ Splits an image into its channels and returns them.

    Arguments:
        *image* (NumPy array) -- the image to be split

        *extra_inputs* (dictionary) -- a dictionary holding any extra inputs
        for the call (empty)

        *parameters* (dictionary) -- a dictionary containing following keys:

            *spectrum* (str, optional) -- the spectrum in which the channels
            will be represented; possible values are *grayscale* and *color*;
            default value is *color*

    Returns:
        list of NumPy array uint8 -- list containing the channels of the image
    """
    if utils.is_color(image):
        b = image[:, :, 0]
        g = image[:, :, 1]
        r = image[:, :, 2]

        if 'spectrum' in parameters:
            spectrum = parameters['spectrum']
        else:
            spectrum = 'color'

        if spectrum == 'color':
            zeros = np.zeros((image.shape[:2]), dtype=np.uint8)
            b = utils.merge_channels([b, zeros, zeros])
            g = utils.merge_channels([zeros, g, zeros])
            r = utils.merge_channels([zeros, zeros, r])

        return [b, g, r]

    return [image]
def channels_shuffle(channels):
    """ Shuffles the channels of an image """
    # Keep shuffling the indices list until a non-stationary permutation has
    # been obtained, as to ensure the output image is not the same as the input
    indices = np.arange(len(channels))
    while (np.arange(len(channels)) == indices).all() and len(channels) != 1:
        np.random.shuffle(indices)

    # Build the output image by merging the channels in the shuffled order
    shuffled_channels = []
    for i in indices:
        shuffled_channels.append(channels[i])

    shuffled_image = utils.merge_channels(shuffled_channels)

    return shuffled_image
Exemple #7
0
def sepia(image, extra_inputs={}, parameters={}):
    """ Applies a **Sepia Filter** onto an image. \n

    Arguments:
        *image* (NumPy array) -- the image to be filtered

        *extra_inputs* (dictionary) -- a dictionary holding any extra inputs
        for the call (empty)

        *parameters* (dictionary) -- a dictionary holding parameter values (empty)

    Returns:
        list of NumPy array uint8 -- list containing the filtered image
    """
    # Apply the Sepia formulas
    if utils.is_color(image):
        result_red = image[:, :,
                           2] * 0.393 + image[:, :,
                                              1] * 0.769 + image[:, :,
                                                                 0] * 0.189
        result_green = image[:, :,
                             2] * 0.349 + image[:, :,
                                                1] * 0.686 + image[:, :,
                                                                   0] * 0.168
        result_blue = image[:, :,
                            2] * 0.272 + image[:, :,
                                               1] * 0.534 + image[:, :,
                                                                  0] * 0.131
    else:
        result_red = image * 0.393 + image * 0.769 + image * 0.189
        result_green = image * 0.349 + image * 0.686 + image * 0.168
        result_blue = image * 0.272 + image * 0.534 + image * 0.131

    # Trim values greater than 255
    result_red = np.where(result_red > 255, 255, result_red)
    result_green = np.where(result_green > 255, 255, result_green)
    result_blue = np.where(result_blue > 255, 255, result_blue)

    # Round the values and convert to int
    result_red = np.uint8(np.rint(result_red))
    result_green = np.uint8(np.rint(result_green))
    result_blue = np.uint8(np.rint(result_blue))

    sepia_image = utils.merge_channels([result_blue, result_green, result_red])

    return [sepia_image]
Exemple #8
0
def build_mosaic(image, texture, technique, alpha, resolution, redundancy):
    """ Helper function which actually builds the mosaic """
    if utils.is_grayscale(image):
        image = utils.merge_channels([image, image, image])
    else:
        image = image[:, :, :3]

    # Load the appropriate image library information
    database_path = os.path.join(project_path, 'backend', 'miscellaneous',
                                 'database')
    pickle_name = texture + '_' + resolution + '.pickle'
    pickle_path = os.path.join(database_path, pickle_name)

    with open(pickle_path, 'rb') as p:
        tiles_height, tiles_width = pickle.load(p)
        tiles_averages_dict = pickle.load(p)

    # Initialise necessary variables
    tiles_averages = np.zeros((len(tiles_averages_dict), 3))
    tiles_averages_values = list(tiles_averages_dict.values())
    tiles_averages_keys = list(tiles_averages_dict.keys())
    for i in range(len(tiles_averages_dict)):
        tiles_averages[i] = np.array(tiles_averages_values[i])

    image_height, image_width = image.shape[:2]

    mosaic_lines = int(image_height / tiles_height)
    mosaic_columns = int(image_width / tiles_width)
    mosaic_image = np.zeros(
        (mosaic_lines * tiles_height, mosaic_columns * tiles_width, 3),
        dtype=np.uint8)
    tile_usage_grid = np.ones(
        (mosaic_lines, mosaic_columns), dtype=np.uint8) * -1

    # For each block:
    #    Compute the average r, g, b values of the block and put them in a vector
    #    Compute the distances between the avg vector of the block and each of the avg vectors of the tiles
    #    Replace the current block with the tile located at the shortest distance from the block
    block_averages = np.zeros((1, 3))
    for line in range(mosaic_lines):
        for column in range(mosaic_columns):
            block = image[line * tiles_height:(line + 1) * tiles_height,
                          column * tiles_width:(column + 1) * tiles_width]
            block_averages[0] = np.array(
                (np.mean(block[:, :, 0]), np.mean(block[:, :, 1]),
                 np.mean(block[:, :, 2])))
            distances = cdist(block_averages, tiles_averages)

            # Determine the index of the closest compatible tile
            if redundancy:
                closest_tile_index = np.where(
                    distances == np.min(distances))[1][0]
            else:
                closest_tile_index = get_closest_usable_tile_index(
                    distances, tile_usage_grid, line, column)

            closest_tile_name = tiles_averages_keys[closest_tile_index]
            closest_tile_path = os.path.join(database_path,
                                             texture + '_' + resolution,
                                             closest_tile_name)
            closest_tile = cv2.imread(closest_tile_path, cv2.IMREAD_UNCHANGED)
            mosaic_image[line * tiles_height:(line + 1) * tiles_height,
                         column * tiles_width:(column + 1) *
                         tiles_width] = closest_tile
            tile_usage_grid[line][column] = closest_tile_index

    if technique == 'alternative':
        # Overlay the mosaic on the input image
        image_copy = utils.resize_dimension(image, *mosaic_image.shape[:2])
        mosaic_image = image_copy * (1 - alpha) + mosaic_image * alpha

    return mosaic_image
Exemple #9
0
def high_pass(image, extra_inputs, parameters):
    """Applies a **High Pass Filter** on an image. \n
    The image is converted into the frequency domain (using the *Fast Fourier
    Transform*) and only the frequencies higher than the cutoff frequency are
    let through. *High-frequency emphasis* can be achieved by providing an *offset*
    greater than 0 (0 is default) and a *multiplier* greater than 1 (1 is default).
    The filter is then transformed by the equation:
        emphasisFilter = offset + multiplier * highpassFilter

    Arguments:
        *image* (NumPy array) -- the image on which the filter is to be applied

        *extra_inputs* (dictionary) -- a dictionary holding any extra inputs
        for the call (empty)

        *parameters* (dictionary) -- a dictionary containing following keys:

            *cutoff* (int) -- the minimum frequency to be let through by the filter

            *offset* (int, optional) -- number used for avoiding the reduction
            of the DC term to 0; default value is 0, which does not prevent
            reduction of the DC term

            *multiplier* (int, optional) -- number used for emphasizing
            frequencies; default value is 1, which does not have any effect

            *type* (str, optional) -- the type of high-pass filter to be applied;
            possible values are: *ideal*, *butterworth*, *gaussian*; default
            value is *gaussian*

            *order* (int, optional) -- the order used for Butterworth filtering;
            default value is 2

            *filename* (str, optional) -- the name of the image file to be
            filtered, used for checking whether the corresponding FFT(s) are
            serialized on the server or not

    Returns:
        list of NumPy array uint8 -- list containing the filtered image
    """
    # Parameter validation and assignment
    if 'offset' in parameters:
        offset = parameters['offset']
    else:
        offset = 0

    if 'multiplier' in parameters:
        multiplier = parameters['multiplier']
    else:
        multiplier = 1

    if 'type' in parameters:
        filter_type = parameters['type']
    else:
        filter_type = 'gaussian'

    if 'order' in parameters:
        order = parameters['order']
    else:
        order = 2

    if 'filename' in parameters:
        filename = parameters['filename']
    else:
        filename = ''

    image_h, image_w = image.shape[:2]  # Take image dimensions

    # Compute the cutoff frequency as a percentage from the smaller dimension of the image
    cutoff_dimension = image_h if image_h < image_w else image_w
    cutoff = parameters['cutoff'] / 100 * cutoff_dimension

    padded_h, padded_w = 2 * image_h, 2 * image_w  # Obtain the padding parameters

    # Check whether the FFTs of the image have been serialized or not
    deserializing, file_not_found = False, False
    pickles_path = os.path.join(project_path, 'webui', 'static', 'tempdata')

    if filename != '':
        filename, extension = filename.split('.')
        if utils.is_color(image):
            files_to_check = [filename + '_' + c + '_fft.pickle' for c in 'bgr']
        else:
            files_to_check = [filename + '_fft.pickle']
        for file in files_to_check:
            if not os.path.isfile(os.path.join(pickles_path, file)):
                file_not_found = True
        if not file_not_found:
            deserializing = True

    # Deserialize the FFTs if possible
    if deserializing:
        padded_image_FFTs = []
        for file in files_to_check:
            f = open(os.path.join(pickles_path, file), 'rb')
            padded_image_FFTs.append(pickle.load(f))
            f.close()
            print('Deserialized', file)
    else:
        # Create padded image
        if utils.is_color(image):
            padded_image = np.zeros((padded_h, padded_w, len(utils.get_channels(image))), np.uint8)
            padded_image[0:image_h, 0:image_w, :] = image
        else:
            padded_image = np.zeros((padded_h, padded_w), np.uint8)
            padded_image[0:image_h, 0:image_w] = image

        # Take the FFTs of the padded image channels
        padded_image_FFTs = utils.get_FFTs(padded_image)

    # Compute the filter image
    if filter_type == 'ideal':
        filter_image = ideal_filter('high', (padded_h, padded_w), cutoff)
    elif filter_type == 'butterworth':
        filter_image = butterworth_filter('high', (padded_h, padded_w), cutoff, order)
    else:
        filter_image = gaussian_filter('high', (padded_h, padded_w), cutoff)

    # Perform High-frequency emphasis
    if multiplier == 1:
        filter_image = offset + filter_image
    else:
        filter_image = offset + np.multiply(multiplier, filter_image)

    # Apply the filter to the FFTs
    filtered_FFTs = [np.multiply(channelFFT, filter_image) for channelFFT in padded_image_FFTs]

    # Take the inverse FFT of the filtered padded image FFT components
    result_components = [np.real(np.fft.ifft2(np.fft.ifftshift(filteredComponent)))
                         for filteredComponent in filtered_FFTs]

    # Obtain the result image
    if len(result_components) == 1:
        result_image = result_components[0]
    else:
        result_image = utils.merge_channels(result_components)

    # Trim values lower than 0 or higher than 255
    result_image = np.where(result_image > 255, 255, result_image)
    result_image = np.where(result_image < 0, 0, result_image)

    # Round the values and unpad the image
    result_image = np.uint8(np.rint(result_image))
    if len(result_components) == 1:
        result_image = result_image[0:image_h, 0:image_w]
    else:
        result_image = result_image[0:image_h, 0:image_w, :]

    return [result_image]
def visible_watermark(image, extra_inputs, parameters):
    """ Embeds a watermark image over a host image, using the visible
    watermarking technique; the watermark is scaled to the selected size and is
    embedded into the selected location, with the selected transparency

    Arguments:
        *image* (NumPy array) -- the image to be watermarked

        *extra_inputs* (dictionary) -- a dictionary holding any extra inputs
        for the call

            *Watermark* (NumPy array) -- the image to be embedded on top of the
            host image

        *parameters* (dictionary) -- a dictionary containing following keys:

            *transparency* (str, optional) -- specifies how will the watermark
            image be overlayed over the host image; possible values are:
            *opaque*, *transparent* and *very transparent*; default value is
            *transparent*

            *location* (str, optional) -- specifies the location where the
            watermark image will be embedded; possible values are: *bottom
            right*, *bottom left*, *top right*, *top left*, *center*,
            *everywhere*; default value is *bottom right*

            *size* (int, optional) -- specifies the maximum width of the
            watermark image, as a percentage of the width of the host image;
            possible values are: 10 to 90, with increments of 10; default value
            is 20 (%)

    Returns:
        list of NumPy array uint8 -- list containing the watermarked image

    Observations:
        If the watermark image's width is already smaller than the maximum
        watermark width computed from *size*, the watermark image will be left
        unchanged. If the height of the watermark image (after width adjustment)
        is greater than the height of the host image, the watermark image will
        be rescaled as to fit the host image
    """
    # Load the extra parameter, the watermark image
    watermark = extra_inputs['Watermark']

    # Parameters extraction
    if 'transparency' in parameters:
        mode = parameters['transparency']
    else:
        mode = 'transparent'

    if 'location' in parameters:
        location = parameters['location']
    else:
        location = 'bottom right'

    if 'size' in parameters:
        size = parameters['size']
    else:
        size = 20

    # Check if the watermark image needs rescaling
    image_h, image_w = image.shape[:2]
    watermark_h, watermark_w = watermark.shape[:2]
    maximum_watermark_width = int(size / 100 * image_w)

    if watermark_w > maximum_watermark_width:
        # Resize watermark image by new width
        watermark = utils.resize_dimension(watermark,
                                           new_width=maximum_watermark_width)
        # The watermark dimensions need to be updated
        watermark_h, watermark_w = watermark.shape[:2]

    if watermark_h > image_h:
        # Resize watermark image by height of host image
        watermark = utils.resize_dimension(watermark, new_height=image_h)

    # If any of the images are grayscale, convert them to the color equivalent
    if utils.is_grayscale(image):
        image = utils.merge_channels([image, image, image])

    if utils.is_grayscale(watermark):
        watermark = utils.merge_channels([watermark, watermark, watermark])

    # Remove the alpha channel from both images (if present)
    if image.shape[2] == 4:
        image = image[:, :, :3]

    if watermark.shape[2] == 4:
        watermark = watermark[:, :, :3]

    # Apply the watermark over the host image; alpha blending technique is used
    # result = background * (1 - alpha) + foreground * alpha
    watermarked_image = image.copy()

    # Compute the alpha level needed for alpha blending
    if mode == 'opaque':
        alpha = 1
    elif mode == 'transparent':
        alpha = 170 / 255
    else:
        alpha = 85 / 255

    # Compute the region of interest, based on the location specified by user
    if location == 'top left':
        line_start = 10
        line_end = watermark_h + 10
        column_start = 10
        column_end = watermark_w + 10
    elif location == 'top right':
        line_start = 10
        line_end = watermark_h + 10
        column_start = image_w - watermark_w - 10
        column_end = image_w - 10
    elif location == 'bottom left':
        line_start = image_h - watermark_h - 10
        line_end = image_h - 10
        column_start = 10
        column_end = watermark_w + 10
    elif location == 'bottom right':
        line_start = image_h - watermark_h - 10
        line_end = image_h - 10
        column_start = image_w - watermark_w - 10
        column_end = image_w - 10
    elif location == 'center':
        line_start = int(image_h / 2 - watermark_h / 2)
        line_end = int(image_h / 2 + watermark_h / 2)
        column_start = int(image_w / 2 - watermark_w / 2)
        column_end = int(image_w / 2 + watermark_w / 2)
    elif location == 'everywhere':
        # Compute number of horizontal and vertical repeats, respectively
        repeat_x = image_w // watermark_w
        repeat_y = image_h // watermark_h

        for x in range(repeat_x):
            for y in range(repeat_y):
                line_start = watermark_h * y
                line_end = watermark_h * (y + 1)
                column_start = watermark_w * x
                column_end = watermark_w * (x + 1)

                watermarked_image[line_start: line_end, column_start: column_end, : 3] = \
                    image[line_start: line_end, column_start: column_end, : 3] * (1 - alpha) + \
                    watermark[:, :, : 3] * alpha

        return [watermarked_image]
    else:
        raise ValueError("'location' parameter value not allowed")

    # Overlay the watermark on the host image
    watermarked_image[line_start: line_end, column_start: column_end, : 3] = \
        image[line_start: line_end, column_start: column_end, : 3] * (1 - alpha) + \
        watermark[:, :, : 3] * alpha

    return [watermarked_image]