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 |
|