3

I'm new in OpenCV, and I will be happy to help me

I have a transparent watermark image like this

enter image description here

And I want to put watermark on bottom-left corner of multiple images with python OpenCV

each images have difference size

and before put I want to resize the watermark to fit the size of the image, that the logo should not be scaled down or up

something like this image:

enter image description here

and here is my code:

import cv2
img1 = cv2.imread('my_image.png')
img2 = cv2.imread('my_watermark.png')
h, w = img1.shape[:2]

rows,cols,channels = img2.shape

roi = img1[0:rows, 0:cols]


img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)


ret, mask = cv2.threshold(img2gray, 220, 255, cv2.THRESH_BINARY_INV)

mask_inv = cv2.bitwise_not(mask)

img1_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)

img2_fg = cv2.bitwise_and(img2,img2,mask = mask)

dst = cv2.add(img1_bg,img2_fg)

img1[0:rows, 0: cols] = dst
cv2.imwrite('imglogo.png', img1)

but I have two problems with this code

first, the watermark is located to the top-right on image

Secondly, the watermark loses its transparency

and the image becomes like this

enter image description here

I try this:

image = cv2.imread('my_image.png')
watermark = cv2.imread('my_watermark.png', cv2.IMREAD_UNCHANGED)

(wH, wW) = watermark.shape[:2]
weight = 1 - watermark[:, :, 3] / 255
num1 = 1250
num2 = 50

image[num1:num1 + wH, num2:num2 + wW, 0] = np.multiply(image[num1:num1 + wH, num2:num2 + wW, 0], weight).astype(np.uint8)
image[num1:num1 + wH, num2:num2 + wW, 1] = np.multiply(image[num1:num1 + wH, num2:num2 + wW, 1], weight).astype(np.uint8)
image[num1:num1 + wH, num2:num2 + wW, 2] = np.multiply(image[num1:num1 + wH, num2:num2 + wW, 2], weight).astype(np.uint8)
output = cv2.addWeighted(image[num1:num1 + wH, num2:num2 + wW], 1, watermark[:, :, 0:3], 1, 1)
image[num1:num1 + wH, num2:num2 + wW] = output
cv2.imwrite("watermark3.png", image)

but in different images, the size of each image changes

so, num1 and num2 must be changed

I was a little confused, how can I put watermark on bottom-left corner on multiple images?

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
shahab-qazavi
  • 362
  • 1
  • 9
  • Does this answer your question? [Python OpenCV - overlay an image with transparency](https://stackoverflow.com/questions/41508458/python-opencv-overlay-an-image-with-transparency) – Christoph Rackwitz Sep 16 '21 at 09:08
  • Some of the code from my answer here https://stackoverflow.com/questions/49070242/converting-images-to-csv-file-in-python/49070833#49070833 might help with reading in multiple images from a directory and also getting the width/height of each (which is what you need for your num1/num2. num1 will be the height of the image minus (the height of the watermark in pixels + whatever border you want)). You'll need to customise it, though. – Pam Sep 16 '21 at 11:07

2 Answers2

1

You are very nearly there with the last code snippet in your question. Your final code mostly works, you just need to generalise the co-ordinates where the watermark goes. In images, the top left corner is co-ordinate (0,0). So if you add the following code, you can work out an appropriate co-ordinate to put the watermark in the bottom left of the image:

(wH, wW) = watermark.shape[:2]
(iH, iW) = image.shape[:2]
border = 50 # allowing a 50 pixel border between the edge of the image and the start of the watermark. 
num1 = iH - (wH + border)
num2 = border

This code allows a fixed 50 pixel border between image edge and watermark start. You might want to use // to set this as a fraction of the image dimensions instead and you might want to use different horizontal and vertical image offsets. Be warned that this code as it stands right now assumes that all your images are at least 50 pixels bigger than your watermark (horizontally and vertically).

If you wanted it at the bottom right of your image, you would simply have:

num1 = iH - (wH + border)
num2 = iW - (wW + border)

You also mention resizing the image. What I suggest you do is figure out what proportion of the image you want the watermark to occupy, so let's say that you want it at least one tenth (1/10) of the image width but you want to maintain the pixel aspect ratio of the watermark:

# Resize the watermark
nwW = int(iW // 10)
nwH = int((nwW/wW) * wH)
resized = cv2.resize(watermark, (nwW, nwH), interpolation = cv2.INTER_AREA)
watermark = resized
wW = nwW
wH = nwH

That \\10 is the "one tenth" setting. Maybe you'll want "one third" \\3 or something different. Do this before you work out the co-ordinates of where the watermark is going but after you've retrieved the original image/watermark dimensions. If you're doing this, you might want to set border as a proportion of the image dimensions, too (rather than a flat 50 pixel border).

You also asked how you would do it on multiple images. For that, you might use this function to create a file list (swap .jpg for whatever your image format actually is):

import os

#Useful function
def createFileList(myDir, format='.jpg'):
fileList = []
print(myDir)
for root, dirs, files in os.walk(myDir, topdown=False):
    for name in files:
        if name.endswith(format):
            fullName = os.path.join(root, name)
            fileList.append(fullName)
return fileList

It comes from an answer here. This will give you a list of file names that you can iterate over, getting the height and width of each once you've read it in with your own code.

Pam
  • 1,146
  • 1
  • 14
  • 18
  • Thank you bro, about resizing watermark I mean, sometimes the image is too large and the watermark is shown in the thumbnail, or sometimes the image is small and the logo is shown in the big image – shahab-qazavi Sep 16 '21 at 21:28
  • Therefore, I have to resize the watermark according to the size of the each image, I hope you understand what I mean – shahab-qazavi Sep 16 '21 at 21:29
  • @shahab-qazavi, I've added some re-sizing code. Resizing the watermark with opencv isn't the challenge, it's knowing the *dimensions* you want to resize it to. I've suggested one possibility but there are others. You must make sure your dimensions are integers, and that your watermark plus the border doesn't exceed the image size (i.e. if you wanted the watermark over the whole image, you would need a border of 0). – Pam Sep 17 '21 at 08:49
0

Here is a solution I had already in C++ which I am sure you can convert to python:

cv::Mat mat = cv::imread("gimp.png", cv::ImreadModes::IMREAD_UNCHANGED);
cv::Mat mat1 = cv::imread("orange.jpg");

cv::Rect cvRectangle;
cvRectangle.x = 0;
cvRectangle.y = 0;
cvRectangle.width = mat.cols;
cvRectangle.height = mat.rows;

if (mat.empty() || mat1.empty())
    return 1;

switch (mat.channels())
{
case 4:
{
    cv::Mat src = mat1(cvRectangle).clone();
    for (int y = 0; y < src.rows; y++)
    {
        const cv::Vec3b* src_pixel = src.ptr<cv::Vec3b>(y);
        const cv::Vec4b* ovl_pixel = mat.ptr<cv::Vec4b>(y);
        cv::Vec3b* dst_pixel = mat1(cvRectangle).ptr<cv::Vec3b>(y);
        for (int x = 0; x < src.cols; x++, ++src_pixel, ++ovl_pixel, ++dst_pixel)
        {
            double alpha = (*ovl_pixel).val[3] / 255.0;
            for (int c = 0; c < 3; c++)
            {
                (*dst_pixel).val[c] = (uchar)((*ovl_pixel).val[c] * alpha + (*src_pixel).val[c] * (1.0 - alpha));
            }
        }
    }
}
    break;
case 3:
{
    cv::Mat src = mat1(cvRectangle).clone();
    cv::addWeighted(src, 0, mat, 1, 0.0, mat1(cvRectangle));
}
    break;
}

cv::imshow("image", mat1);
cv::waitKey(0);

return 0;

Gives me the following result: enter image description here

guivi
  • 382
  • 1
  • 5