在 python opencv 中的多个图像的左下角放置水印

put watermark on bottom left on multiple images in python opencv

我是 OpenCV 新手, 我会很乐意帮助我

我有一张这样的透明水印图片

我想用 python OpenCV

在多张图片的左下角添加水印

每张图片大小不一

在放置之前我想调整水印的大小以适应图像的大小,徽标不应按比例缩小或放大

类似于这张图片:

这是我的代码:

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)

但是这段代码有两个问题

首先,水印位于图片的右上角

其次,水印失去透明度

图像变成这样

我试试这个:

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)

但在不同的图片中,每张图片的大小都会发生变化

所以,必须更改 num1 和 num2

我有点困惑, 如何在多张图片的左下角添加水印?

这是我在 C++ 中已有的解决方案,我相信您可以将其转换为 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;

给我以下结果:

您的问题中的最后一个代码片段就快完成了。您的最终代码大部分都有效,您只需要概括水印所在的坐标。在图像中,左上角是坐标 (0,0)。所以如果你添加下面的代码,你可以计算出一个合适的坐标将水印放在图片的左下角:

(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

此代码允许图像边缘和水印开始之间有一个固定的 50 像素边界。您可能希望使用 // 将其设置为图像尺寸的一部分,并且您可能希望使用不同的水平和垂直图像偏移量。请注意,此代码目前假定您的所有图像至少比水印大 50 像素(水平和垂直)。

如果您希望它位于图片的右下角,您只需:

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

您还提到调整图像大小。我建议你做的是弄清楚你想要水印占据图像的比例,所以假设你想要它至少是图像宽度的十分之一(1/10)但您想保持水印的像素纵横比:

# 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

\10就是“十分之一”的设定。也许您会想要“三分之一”\3 或其他内容。在之前你计算出水印去向的坐标但是在之后你已经检索到原始的image/watermark尺寸.如果您这样做,您可能还想将 border 设置为图像尺寸的比例(而不是平坦的 50 像素边框)。

您还询问了如何在多个图像上进行处理。为此,您可以使用此功能创建一个文件列表(无论您的图像格式实际是什么,请交换 .jpg):

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

来自回答。这将为您提供一个文件名列表,您可以遍历这些文件名,并在您使用自己的代码读入后获取每个文件的高度和宽度。