Image Processing #4 - WaterMark without alpha channel


A few days ago, I wrote a blog post on pasting overlays with OpenCV. A little tweaking the code in that article can create a watermark feature.
Many Python samples that implement watermarks use the alpha channel of png files. However, I would like to implement the function of generating a watermark using an image of an RGB channel without an alpha channel.


Masking code in my previous blog



def process_masking(base, mask, pos):
    h, w, c = mask.shape
    hb, wb, _ = base.shape
    x = pos[0]
    y = pos[1]

    #check mask position
    if(x > wb or y > hb):
        print(' invalid overlay position(%d,%d)'%(x, y))
        return None
    
    #remove alpha channel    
    if c == 4:
        mask = cv2.cvtColor(mask, cv2.COLOR_BGRA2BGR) 
    
    #adjust mask
    if(x + w > wb):
        mask = mask[:, 0:wb - x]
        print(' mask X size adjust[W:%d] -> [W:%d]'%(w, wb - x))
    if(y + h > hb):
        mask = mask[0:hb - y, :]
        print(' mask Y size adjust[H:%d] -> [H:%d]'%(h, hb - y))

    h, w, c = mask.shape
    
    img = base.copy()
    bg = img[y:y+h, x:x+w]      #overlay area
    try:
        for i in range(0, h):
            for j in range(0, w):
                B = mask[i][j][0]
                G = mask[i][j][1]
                R = mask[i][j][2]
                if (int(B) + int(G) + int(R)):
                    bg[i][j][0] = B
                    bg[i][j][1] = G
                    bg[i][j][2] = R
        img[y:y+h, x:x+w] = bg
    except IndexError:  #index (i, j) is out of the screen resolution.  
        print(' index Error')
        return None
    return img


This code replaces the pixel values ​​of the original image area with mask value if the RGB value of the  mask exist. If you do not replace the pixel values ​​of the original image with the mask values, mix original image pixel values ​​with mask image pixel values.


New watermark function

This function is very similar to the above function. But it mixes the original pixel and the mask pixel by the mixing ratio as a parameter(rate). The sum of the reflectance ratios of the original image pixels and the watermark image pixels is always one.


def watermark(img, mask, pos, rate):
    h, w, c = mask.shape
    x = pos[0]
    y = pos[1]
    height, width, channels = img.shape

    if (x + w) > width :
        print('Error : The worker mark is out of the image area.')
        return None
    if c == 4:
        mask = cv2.cvtColor(mask, cv2.COLOR_BGRA2BGR) 
     #adjust mask
    if(x + w > wb):
        mask = mask[:, 0:wb - x]
        print(' mask X size adjust[W:%d] -> [W:%d]'%(w, wb - x))
    if(y + h > hb):
        mask = mask[0:hb - y, :]
        print(' mask Y size adjust[H:%d] -> [H:%d]'%(h, hb - y))

    h, w, c = mask.shape
    img = base.copy()
    bg = img[y:y+h, x:x+w]      #overlay area
    try:
        for i in range(0, h):
            for j in range(0, w):
                B = mask[i][j][0]
                G = mask[i][j][1]
                R = mask[i][j][2]
                if (int(B) + int(G) + int(R)):
                    bg[i][j][0] = float(B) * rate + float(bg[i][j][0]) * (1 - rate)
                    bg[i][j][1] = float(G) * rate + float(bg[i][j][1]) * (1 - rate)
                    bg[i][j][2] = float(R) * rate + float(bg[i][j][2]) * (1 - rate)
        bg.astype('uint8')            
        img[y:y+h, x:x+w] = bg
    except IndexError:
        print(' index Error')
        return None
    return img

The values ​​in the bg array ​​changed to floating point numbers during the operation. Therefore, you must convert to uint8 type again after all operations using astype function. Every coloe image has at least three channels(R, G, B). PNG files can have an alpha channel, which in this case is 4. All values ​​in the image array are between 0 and 255. The variable type is uint8.

Full Code



import argparse
import cv2
import numpy as np
'''
This function remains background
rate should be : 0 < rate < 1.0
'''
def watermark(img, mask, pos, rate):
    h, w, c = mask.shape
    x = pos[0]
    y = pos[1]
    height, width, channels = img.shape

    if (x + w) > width :
        print('Error : The worker mark is out of the image area.')
        return None
    if c == 4:
        mask = cv2.cvtColor(mask, cv2.COLOR_BGRA2BGR) 
    #adjust mask
    if(x + w > wb):
        mask = mask[:, 0:wb - x]
        print(' mask X size adjust[W:%d] -> [W:%d]'%(w, wb - x))
    if(y + h > hb):
        mask = mask[0:hb - y, :]
        print(' mask Y size adjust[H:%d] -> [H:%d]'%(h, hb - y))

    h, w, c = mask.shape
    img = base.copy() 
    bg = img[y:y+h, x:x+w]      #overlay area
    try:
        for i in range(0, h):
            for j in range(0, w):
                B = mask[i][j][0]
                G = mask[i][j][1]
                R = mask[i][j][2]
                if (int(B) + int(G) + int(R)):
                    bg[i][j][0] = float(B) * rate + float(bg[i][j][0]) * (1 - rate)
                    bg[i][j][1] = float(G) * rate + float(bg[i][j][1]) * (1 - rate)
                    bg[i][j][2] = float(R) * rate + float(bg[i][j][2]) * (1 - rate)
        bg.astype('uint8')            
        img[y:y+h, x:x+w] = bg
    except IndexError:
        print(' index Error')
        return None
    return img


parser = argparse.ArgumentParser(description="OpenCV Example")
parser.add_argument("--file", type=str, required=True, help="filename of the input image to process")
parser.add_argument("--mask", type=str, required=True, help="mask image to overlay")
parser.add_argument("--rate", type=float, default=0.5, help="mixing rate")
args = parser.parse_args()

img = cv2.imread(args.file, cv2.IMREAD_COLOR)
height, width, channels = img.shape
print("image   H:%d W:%d, Channel:%d"%(height, width, channels))
cv2.imshow('original', img)

mark = cv2.imread(args.mask, cv2.IMREAD_UNCHANGED)
mheight, mwidth, mchannels = mark.shape
print("mask   H:%d W:%d, Channel:%d"%(mheight, mwidth, mchannels))

x = np.amin( [mwidth, width])
y = np.amin( [mheight, height])
new_img = watermark(img, mark, (x, y), args.rate)
if new_img is not None :
    cv2.imshow('masked', new_img)
cv2.waitKey(0)

Run the code.


python watermark.py --file=biden.jpg --mask=watermark.png
image   H:727 W:320, Channel:3
mask   H:32 W:100, Channel:4



You can adjust --rate value to adjust the clearness of the watermark.


python watermark.py --file=biden.jpg --mask=watermark.png --rate=0.8
image   H:727 W:320, Channel:3
mask   H:32 W:100, Channel:4

Default rate value is 0.5, if you increase this value to 0.8, the watermark image is more clear and the background image is hard to see.



Wrapping up

This article is an example of inserting a watermark using a jpg image without an alpha channel. If it is a png file with an alpha channel, the alpha channel is removed and a watermark is inserted. Therefore, there is a rate variable that controls the concentration in the parameter of the watermark function. However, if an alpha channel exists, the density can be adjusted according to the size of the alpha channel value. See the next article for an example of a watermark using the alpha channel.


You can download the source codes here(https://github.com/raspberry-pi-maker/OpenCV)

댓글

이 블로그의 인기 게시물

Image Processing #7 - OpenCV Text

Playing YouTube videos using OpenCV

OpenCV Installation - Rasbian Buster, Jessie, DietPi Buster