Creating a custom color filter

Would you like to create a more sophisticated filter and apply it to your images? We will put everything we have learned so far together to create a color-splash filter effect. The color-splash filter effect is achieved by using a spot or area representing the colors that are going to be retained. This is represented in the following image:

Let's start by loading an image. This time, we will use a Busan night scene from the sample-images folder. This provides us with a wide range of colors, and the splash effect will be prominent. We will load the image as follows:

using Images, ImageView

# load an image and create a grayscale copy
img = load("sample-images/busan-night-scene.jpg");
img_gray = RGB.(Gray.(img))

A few points to note here are the following:

Let's proceed by representing the image objects in three channels using the channelview function. We will also change the order of the dimensions and put the channel dimension last. Consider the following code:

# get channels representation
img_channel_view = channelview(img);
img_gray_channel_view = channelview(img_gray);

# make channel dimension last and crop the required are
img_arr = permuteddimsview(img_channel_view, (2, 3, 1));
img_gray_arr = permuteddimsview(img_gray_channel_view, (2, 3, 1));

We will be using mask to keep track of the pixels and colors we want to keep. Initially, we set it to keep all the pixels:

# create a mask with all values being true
img_mask = fill(true, size(img));

Before proceeding with updating mask, we need to define a spot area in the original image. We will use the area to extract the colors we want to keep. You can change the value to anything as long as it fits the original area and previews it. This is done as follows:

# spot are with colors to retain
img_spot_height = 430:460
img_spot_width = 430:460

# preview color area (optional)
imshow(img[img_spot_height, img_spot_width])

The next part will be the most complicated. We will be executing multiple actions in one run. Consider doing the following:

  1. Iterate over three channels of our image in a for loop and work on each one separately
  2. Crop the area of interest from a channel
  3. Identify the minimum and maximum color in the area
  4. Create a channel mask by checking whether the channel color value falls within the minimum/maximum range
  5. Apply the logical AND operation on image and channel masks

The code for the preceding actions is as follows:

for channel_id = 1:3

# select current channel and crop areas of interest
current_channel = view(img_arr, :, :, channel_id)
current_channel_area = current_channel[img_spot_height,
img_spot_width, :]

# identify min and max values in a cropped area
channel_min = minimum(current_channel_area)
channel_max = maximum(current_channel_area)

# merge existing mask with a channel specific mask
channel_mask =
channel_min .< current_channel .< channel_max
img_mask = img_mask .& channel_mask
end

The final step is to apply img_mask and merge source image with grayscale:

img_masked = img_arr .* img_mask .+ img_gray_arr .* .~(img_mask);
final_image = colorview(RGB, permutedims(img_masked, (3, 1, 2)))
imshow(final_image)