| 1 | #=======================================================================
|
| 2 | # Non-convolutional image filtering by bitmasks
|
| 3 | #
|
| 4 | # Cesare Brizio, 20 January 2023
|
| 5 | #
|
| 6 | # Attempt to split an image in three separate images,
|
| 7 | # - one with the darkest colors
|
| 8 | # - one with midtone colors
|
| 9 | # - one with the brightest colors
|
| 10 | #
|
| 11 | # "cat image" 1.jpg is available as a part of the
|
| 12 | # Cats vs. Dogs dataset from Kaggle
|
| 13 | # (https://www.kaggle.com/datasets/pybear/cats-vs-dogs?select=PetImages)
|
| 14 | #=======================================================================
|
| 15 |
|
| 16 | import numpy as np
|
| 17 | from PIL import Image, ImageOps
|
| 18 | from matplotlib import pyplot as plt
|
| 19 | from matplotlib import image as mpimg
|
| 20 | from matplotlib import colors as mcolors
|
| 21 | from numpy import asarray
|
| 22 | import cv2
|
| 23 |
|
| 24 | def plot_image(img: np.array):
|
| 25 | plt.figure(figsize=(6, 6), dpi=96)
|
| 26 | plt.title("Cat Image")
|
| 27 | plt.xlabel("X pixel scaling")
|
| 28 | plt.ylabel("Y pixels scaling")
|
| 29 | #plt.imshow(img, cmap='gray');
|
| 30 | plt.imshow(img);
|
| 31 | plt.show()
|
| 32 |
|
| 33 | def plot_two_images(img1: np.array, img2: np.array, imm_name):
|
| 34 | _, ax = plt.subplots(1, 2, figsize=(12, 6), dpi=96)
|
| 35 | plt.title(imm_name)
|
| 36 | plt.xlabel("X pixel scaling")
|
| 37 | plt.ylabel("Y pixels scaling")
|
| 38 | #ax[0].imshow(img1, cmap='gray')
|
| 39 | #ax[1].imshow(img2, cmap='gray');
|
| 40 | ax[0].imshow(img1)
|
| 41 | ax[1].imshow(img2);
|
| 42 | plt.show()
|
| 43 |
|
| 44 | # Bitwise AND detects coincidence with different combination of
|
| 45 | # channel bits:
|
| 46 | Mask_Lightest_Pass = 0x80 #top bit high for 8-bit bitwise AND
|
| 47 | Mask_Darkest_Pass = 0x07 #bottom 3 bit high for 8-bit bitwise AND
|
| 48 | Mask_HIGH_3_BIT = 0xE0 #top three bit high for 8-bit bitwise AND
|
| 49 | Mask_HIGH_2_BIT = 0xC0 #top two bit high for 8-bit bitwise AND
|
| 50 |
|
| 51 |
|
| 52 | def BITMASK_LIGHTEST(img: np.array) -> np.array:
|
| 53 |
|
| 54 | h, w, c = img.shape
|
| 55 |
|
| 56 | #========> Empty image with all white pixels
|
| 57 | #light_img = np.zeros(shape=(h, w, c))
|
| 58 | light_img = np.full(img.shape,0xFF)
|
| 59 |
|
| 60 | # Iterate over the rows
|
| 61 | for i in range(h):
|
| 62 | # Iterate over the columns
|
| 63 | for j in range(w):
|
| 64 | # img[i, j] = individual pixel values
|
| 65 | # Get the current pixel one channel at a time
|
| 66 | AND_BRIGHT_B = img[i, j, 0] & Mask_Lightest_Pass
|
| 67 | AND_BRIGHT_G = img[i, j, 1] & Mask_Lightest_Pass
|
| 68 | AND_BRIGHT_R = img[i, j, 2] & Mask_Lightest_Pass
|
| 69 | #print("AND_B ",AND_B," AND_G ",AND_G," AND_R ",AND_R)
|
| 70 | # Check if in the top range (Mask_Light_Pass = 0x808080)
|
| 71 | if AND_BRIGHT_B == Mask_Lightest_Pass and AND_BRIGHT_G == Mask_Lightest_Pass and AND_BRIGHT_R == Mask_Lightest_Pass:
|
| 72 | # Store the result to i-th row and j-th column of light_img
|
| 73 | for c in range(3):
|
| 74 | light_img[i, j, c] = img[i, j, c]
|
| 75 |
|
| 76 | # Clip result array to range 0..255 and make into uint8
|
| 77 | result = np.clip(light_img, 0, 255).astype(np.uint8)
|
| 78 |
|
| 79 | return result
|
| 80 |
|
| 81 | def BITMASK_DARKEST(img: np.array) -> np.array:
|
| 82 |
|
| 83 | h, w, c = img.shape
|
| 84 |
|
| 85 | #========> Empty image with all white pixels
|
| 86 | #dark_img = np.zeros(shape=(h, w, c))
|
| 87 | dark_img = np.full(img.shape,0xFF)
|
| 88 |
|
| 89 | # Iterate over the rows
|
| 90 | for i in range(h):
|
| 91 | # Iterate over the columns
|
| 92 | for j in range(w):
|
| 93 | # img[i, j] = individual pixel value
|
| 94 | # Get the current pixel one channel at a time
|
| 95 | AND_DARK_B = img[i, j, 0] & Mask_HIGH_2_BIT
|
| 96 | AND_DARK_G = img[i, j, 1] & Mask_HIGH_2_BIT
|
| 97 | AND_DARK_R = img[i, j, 2] & Mask_HIGH_2_BIT
|
| 98 | #print("AND_B ",AND_B," AND_G ",AND_G," AND_R ",AND_R)
|
| 99 | # Check if in the low range (Mask_Light_Pass = 0x070707)
|
| 100 | if (AND_DARK_B + AND_DARK_G + AND_DARK_R) == 0:
|
| 101 | # Store the result to i-th row and j-th column of light_img
|
| 102 | for c in range(3):
|
| 103 | dark_img[i, j, c] = img[i, j, c]
|
| 104 |
|
| 105 | # Clip result array to range 0..255 and make into uint8
|
| 106 | result = np.clip(dark_img, 0, 255).astype(np.uint8)
|
| 107 |
|
| 108 | return result
|
| 109 |
|
| 110 | def BITMASK_MIDTONES(img: np.array) -> np.array:
|
| 111 |
|
| 112 | h, w, c = img.shape
|
| 113 |
|
| 114 | #========> Empty image with all white pixels
|
| 115 | #mid_img = np.zeros(shape=(h, w, c))
|
| 116 | mid_img = np.full(img.shape,0xFF)
|
| 117 |
|
| 118 | # Iterate over the rows
|
| 119 | for i in range(h):
|
| 120 | # Iterate over the columns
|
| 121 | for j in range(w):
|
| 122 | # img[i, j] = individual pixel value
|
| 123 | # Get the current pixel one channel at a time
|
| 124 | # check that it's not dark
|
| 125 | AND_DARK_B = img[i, j, 0] & Mask_HIGH_2_BIT
|
| 126 | AND_DARK_G = img[i, j, 1] & Mask_HIGH_2_BIT
|
| 127 | AND_DARK_R = img[i, j, 2] & Mask_HIGH_2_BIT
|
| 128 | #print("AND_B ",AND_B," AND_G ",AND_G," AND_R ",AND_R)
|
| 129 | # Check if in the low range (Mask_Light_Pass = 0x070707)
|
| 130 | if (AND_DARK_B + AND_DARK_G + AND_DARK_R) == 0:
|
| 131 | #----------------------------
|
| 132 | # THIS IS A DARK PIXEL !!!
|
| 133 | #----------------------------
|
| 134 | pass
|
| 135 | else:
|
| 136 | # check that it's not bright
|
| 137 | AND_BRIGHT_B = img[i, j, 0] & Mask_Lightest_Pass
|
| 138 | AND_BRIGHT_G = img[i, j, 1] & Mask_Lightest_Pass
|
| 139 | AND_BRIGHT_R = img[i, j, 2] & Mask_Lightest_Pass
|
| 140 | #print("AND_B ",AND_B," AND_G ",AND_G," AND_R ",AND_R)
|
| 141 | # Check if in the top range (Mask_Light_Pass = 0x808080)
|
| 142 | if AND_BRIGHT_B == Mask_Lightest_Pass and AND_BRIGHT_G == Mask_Lightest_Pass and AND_BRIGHT_R == Mask_Lightest_Pass:
|
| 143 | #----------------------------
|
| 144 | # THIS IS A BRIGHT PIXEL !!!
|
| 145 | #----------------------------
|
| 146 | pass
|
| 147 | else:
|
| 148 | # Store the result to i-th row and j-th column of light_img
|
| 149 | for c in range(3):
|
| 150 | mid_img[i, j, c] = img[i, j, c]
|
| 151 |
|
| 152 | # Clip result array to range 0..255 and make into uint8
|
| 153 | result = np.clip(mid_img, 0, 255).astype(np.uint8)
|
| 154 |
|
| 155 | return result
|
| 156 |
|
| 157 |
|
| 158 | #=======================================================
|
| 159 | # LOAD THE ORIGINAL IMAGE by Image.open method
|
| 160 | #=======================================================
|
| 161 | pI = Image.open('C:/Conv_Python/images/1.jpg')
|
| 162 |
|
| 163 | # create numpy array from image
|
| 164 | img = np.array(pI)
|
| 165 |
|
| 166 | #plot_image(img=img)
|
| 167 | #image_to_save = Image.fromarray(img,'RGB')
|
| 168 | #image_to_save.save("original_image.jpg")
|
| 169 |
|
| 170 |
|
| 171 | #============================================
|
| 172 | # Save only lightest pixels with the
|
| 173 | # most significant bit high in each
|
| 174 | # channels (upper half of range)
|
| 175 | # AND succeds with Mask_Lightest_Pass = 0x80
|
| 176 | #============================================
|
| 177 | Curr_Title="Cat Image - LIGHTEST PIXELS (HIGHLIGHTS)"
|
| 178 | img_lightest_pix = BITMASK_LIGHTEST(img=np.array(img))
|
| 179 |
|
| 180 |
|
| 181 | plot_two_images(
|
| 182 | img1=img,
|
| 183 | img2=img_lightest_pix,
|
| 184 | imm_name=Curr_Title
|
| 185 | )
|
| 186 |
|
| 187 | plt.imsave(fname='lightest_pixels.png', arr=img_lightest_pix, format='png')
|
| 188 |
|
| 189 |
|
| 190 | #=======================================
|
| 191 | # Save only pixels with all three
|
| 192 | # channels having the two most
|
| 193 | # significant bits low
|
| 194 | # AND fails with Mask_HIGH_2_BIT = 0xC0
|
| 195 | #=======================================
|
| 196 | Curr_Title="Cat Image - DARKEST PIXELS (SHADOWS)"
|
| 197 | img_darkest_pix = BITMASK_DARKEST(img=np.array(img))
|
| 198 |
|
| 199 |
|
| 200 | plot_two_images(
|
| 201 | img1=img,
|
| 202 | img2=img_darkest_pix,
|
| 203 | imm_name=Curr_Title
|
| 204 | )
|
| 205 |
|
| 206 | plt.imsave(fname='darkest_pixels.png', arr=img_darkest_pix, format='png')
|
| 207 |
|
| 208 |
|
| 209 |
|
| 210 | #==========================================
|
| 211 | # Save the pixels not selected previously
|
| 212 | #==========================================
|
| 213 | Curr_Title="Cat Image - MIDTONE PIXELS (MIDTONES)"
|
| 214 | img_midtone_pix = BITMASK_MIDTONES(img=np.array(img))
|
| 215 |
|
| 216 |
|
| 217 | plot_two_images(
|
| 218 | img1=img,
|
| 219 | img2=img_midtone_pix,
|
| 220 | imm_name=Curr_Title
|
| 221 | )
|
| 222 |
|
| 223 | plt.imsave(fname='midtone_pixels.png', arr=img_midtone_pix, format='png')
|
| 224 |
|