热门标签 | HotTags
当前位置:  开发笔记 > 程序员 > 正文

给图片加水印手把手教新码农如何把技术变成产品

前言加水印是为图片声明版权出处的一种常用方法。平常都是写技术文章,文章的重点在技术本身,照片往往不需要加水印,或者需要加也不多,祭出神器PhotoShop很快就能完成。前一段

问题并不复杂,打开图片和作为水印的logo,然后再读取图片中作为镂空的背景部分。接着把logo镂空部分去除,然后复制到目标图片上就完成了工作,主要的工作代码只有7行。
主要函数使用copyTo,点击链接是opencv官方的说明文档。
opencv的编译,需要在命令行给出头文件和链接库的额外参数,建议写一个脚本来编译,这里也贴出来(本例中使用当前的opencv4):

#!/bin/bash

g++ -std=c++11 -o $1 $1.cpp `pkg-config --cflags --libs opencv4` 

使用脚本来编译和执行使用如下命令(假设源码名称为wmv1.cpp):

$ ./mkcv4.sh wmv1
$ ./wmv1

在一张样本的图片上运行这个程序,得到的结果效果如下:
版本2

不管有多么不高兴,生活总要继续,工作也得推动下去。
其实用户挑毛病永远不是最可怕的,可怕的是用户不挑毛病,并且还不买单。
所以既然用户有反馈,我们逐条解决就好了。
首先看“水印效果”的问题,opencv中有专门的函数addWeighted处理两幅图片之间的重叠互动问题。用起来更简单,连蒙版mask部分都不需要了:

	const float _alpha=0.5;

	Mat image = imread(picfile);
	Mat logo = imread(logofile);
	Mat imageROI;

	imageROI = image(Rect(mx,my,logo.cols,logo.rows));
	addWeighted(imageROI, 1.0, logo, _alpha, 0, imageROI);
	imwrite(outputfile,image);

水印尺寸偏大的问题,水印文件本身肯定是固定的。但在大的图片中,水印肯定显得小,小的图片中,水印就会显得大。因此需要水印图片的尺寸是可以变化的,是一个合理的需求。
opencv中调整图片的尺寸很容易,我们可以要求用户输入一个水印logo尺寸的宽度,随后保持logo的比例,计算出来logo的新高度。然后调整logo的尺寸就可以了。

    int neww,newh;
    neww = (int)_logowidth;
    newh = (int)(logo.rows * ((float)neww / logo.cols));
    Size dsize=Size(neww,newh);
    resize(logo,logo,dsize);

文件名、logo位置问题,都可以由程序运行时,用户输入的参数来确定,这个再简单不过。
很快,第二版新鲜出炉:

#include 
#include 
#include 
#include 

using namespace std;
using namespace cv;

#define PATH_MAX 1024

const float _alpha=0.5;

char _picfile[PATH_MAX];
char _outputfile[PATH_MAX];
char _logofile[PATH_MAX];
int _logowidth;
int _mx,_my;

int main(int argc, char **argv){
    if (argc != 7) {
        printf("Wrong parament!\n");
        return 1;
    }

    strcpy(_picfile,argv[1]);
    strcpy(_outputfile,argv[2]);
    strcpy(_logofile,argv[3]);
    _logowidth=atol(argv[4]);
    _mx=atol(argv[5]);
    _my=atol(argv[6]);


    Mat image = imread(_picfile);
    Mat logo = imread(_logofile);
    Mat imageROI;

    int neww,newh;
    neww = (int)_logowidth;
    newh = (int)(logo.rows * ((float)neww / logo.cols));
    Size dsize=Size(neww,newh);
    resize(logo,logo,dsize);

    imageROI = image(Rect(_mx,_my,logo.cols,logo.rows));
    addWeighted(imageROI, 1.0, logo, _alpha, 0, imageROI);
    imwrite(_outputfile,image);
}

我们再次编译、执行来试一试:

$ ./mkcv4.sh wmv2
$ ./wmv2 IMG_20190521_125150.jpg IMG_20190521_125150-logoed.jpg logo.png 150 100 100

得到的图片如下:
3) || (_position<0)) _position=0; break; case \'c\': _copy = 1; //meas true break; default: usage(); } } if (strlen(_srcFilename) == 0) { usage(); exit(1); }; if (strlen(_dstFilename) == 0) { addPostfix(_srcFilename,_dstFilename); }; return 0; } /* position = 0, logo on right,bottom position = 1, logo on left,bottom position = 2, logo on left,top position = 3, logo on right,top */ void getPosition(int position,Mat image,Mat logo,int *X,int *Y){ // x/y _margin using image.cols,not rows switch(position){ case 0: *X=(image.cols-logo.cols) - (image.cols * _margin); *Y=(image.rows-logo.rows) - (image.cols * _margin); break; case 1: *X=image.cols * _margin; *Y=(image.rows-logo.rows) - image.cols * _margin; break; case 2: *X=image.cols * _margin; *Y=image.cols * _margin; break; case 3: *X=(image.cols-logo.cols) - (image.cols * _margin); *Y=image.cols * _margin; break; default: *X=(image.cols-logo.cols) - (image.cols * _margin); *Y=(image.rows-logo.rows) - (image.cols * _margin); break; }; return; } void markIt(const char *srcpic, const char *logopic, const char *dstpic, int position=0){ Mat image = imread(srcpic); Mat logo = imread(logopic); Mat imageROI; int markx,marky; Mat mask=imread(logopic,0); if (_scale <1){ float scale=(image.cols * _scale) / logo.cols; Size dsize=Size(logo.cols*scale,logo.rows*scale); resize(logo,logo,dsize); resize(mask,mask,dsize); } else if(_scale > 1) { int neww,newh; neww = (int)_scale; newh = (int)(logo.rows * ((float)neww / logo.cols)); Size dsize=Size(neww,newh); resize(logo,logo,dsize); resize(mask,mask,dsize); }; logo.rows); getPosition(position,image,logo,&markx,&marky); imageROI = image(Rect(markx,marky,logo.cols,logo.rows)); if (_copy){ logo.copyTo(imageROI,mask); } else { addWeighted(imageROI, 1.0, logo, _alpha, 0, imageROI); } imwrite(dstpic,image); } int main(int argc, char **argv){ getOptions(argc,argv); dumpDefault(); markIt(_srcFilename,_logoFilename,_dstFilename,_position); return 0; }

从完成的程序代码上看同样也是如此,大量的代码都是用于处理参数和默认值逻辑,实际加水印的代码,几乎没有什么变化。

技术人员不能只沉迷于技术,技术人员的升职加薪,往往得益于其它经验的积累,比如行业经验,比如沟通协调经验。

假设我们当前目录准备了一张图片叫DSCF2183.jpg:

这是最简的运行模式,只需要一个输入文件。水印文件自动缩放到目标图片宽度的30%,然后透明叠加在右下角:


补充

作为一个命令行程序,第三版已经基本可以满足应用见用户了。忘了提醒你注意附加在程序内部的程序使用文档,千万注意保证文档的完善、准确。很多优秀的产品,用户能不能用的好,往往是由文档的水平决定的。
回到最初的话题,如果是自己作为这个用户,那还有一个小需求没有被满足。那就是,我的图片量很大,并且分布在多篇游记的复杂目录结构中。如何同时为多幅图片添加水印?
这算的上非常个性化的需求,当然可以实现在程序中。但在没有大量用户支持的情况下,这种需求可能只是增加了程序的复杂度,但并没有多少人用。
对于这种需求,完全可以使用外围脚本的形式来解决。使用bash写这样的脚本,也不过几行代码而已:

#!/bin/bash

files=$(find $1 -name "*jpg" -o -name "*png" -o -name "*jpeg")

for file in $files
do
    wmv3 -i $file -o $file
done

把脚本设置为可执行,然后把脚本和主程序都拷贝到系统的可执行文件夹:

$ chmod +x markall.sh
$ sudo cp markall.sh /usr/bin
$ sudo cp wmv3 /usr/bin

这次为再多的图片加水印也不怕了,比如我们有一个测试文件夹,是这样的结构:

只要如此执行就可以为文件夹下面,及其子文件夹中所有的jpg/jpeg/png文件添加水印:

$ markall.sh test

至此,才可以真的完活,收工!


推荐阅读
author-avatar
mobiledu2502859097
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有