01/20/23 07:09:26 C:\Conv_Python\SEPARATE_HIGH_MID_SHAD.py
   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