批量为图片增加水印

由于最近需要向博客里传图片,传之前需要打水印,图片一多打水印工作就变得很麻烦,考虑到未来的日子里还有无穷无尽的图片需要加水印,即使在这个图片处理软件遍地都是的时代,鼠标点来点去也是一件很头痛的事情,所以干脆写个程序一次性解决图片加水印的问题。
图像处理自然就需要opencv(虽然搞个opencv,只为了给图片加水印,有点儿大炮打蚊子的嫌疑)。
本来打算在windows上用VS2013+opencv的,结果兴冲冲地打开许久不用的2013,居然告诉我License过期了!!!
说好的社区版免费呢?枉费我专门到官网找安装包,辛辛苦苦下载了好久,不得不说微软有些时候实在太小气,最终还是改用ubuntu。
打开下载页面发现,继3.1版本之后居然出了一个2.4.13,相传是为了照顾熟悉老架构的开发人员,我不想技术考古,所以还是选择用3.1。
安装过程网上教程一大把,就不重复了:
QT我没装,ubuntu下的IDE还是用不惯啊。
==============================
(1) 文字水印
一个函数搞定:
1
2
3
void putText(Mat& img, const string& text, Point org, int fontFace, 
	double fontScale, Scalar color, int thickness=1, int lineType=8, 
	bool bottomLeftOrigin=false );
里面有几个关键的参数:
  • img:目标图片矩阵;
  • text:想要叠加的文字;
  • org:叠放的偏移;
  • frontFace:字体;
  • fontScale:文字尺寸;
  • color:文字颜色
  • 其他参数请自行查看手册;
(2) 图片水印
首先,需要一张LOGO图片:
1
Mat logo = imread("./logo.png");
然后,在目标图片image上选一个LOGO图片希望覆盖的区域,这个区域必须和LOGO大小相同:
1
image_roi = image(cv::Rect(x, y, width, height));
最后,有两个方法可以将LOGO覆盖到选定区域:copyTo和cvAddWeighted
copyTo函数是Mat对象的一个内置方法:
1
void Mat::copyTo( OutputArray _dst, InputArray _mask ) const;
有两个参数:
  • _dst:目标图片矩阵;
  • _mask:掩码矩阵;
copyTo的缺点是不够灵活会直接覆盖到原来的图片上,导致看不到覆盖下方的图片原貌;
所以我更喜欢另一个函数:
1
2
cvAddWeighted( const CvArr* src1, double alpha,  
	const CvArr* src2, double beta,  double gamma, CvArr* dst );
网上有很多介绍,这里就不多说了。
接下来为了能够方便使用,而且考虑到随着对opencv研究的深入,还要进一步改进代码,所以我对几个函数进行了简单的封装(由于程序很简单就不加注释了):
头文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*************************************************************************
	> File Name: water.h
	> Author: Dong ZhiChao
	> Mail: 
	> Created Time: Sun 04 Dec 2016 01:20:51 PM CST
 ************************************************************************/
#ifndef _WATER_H
#define _WATER_H
 
#include <opencv2/opencv.hpp>       // 注意:使用"/",和windows不同
#include 
 
#define SHOW_WINDOW_NAME    "water_print"
 
using namespace cv;
using namespace std;
 
namespace mars{
	class water_print{
	public:
		water_print(string &file_in);
		~water_print();
 
	public:
		int show(void);
		int text(string &logo_str, int x, int y);
		int image(string &logo_img, double alpha);
		int write(string &file_out);
 
	private:
		Mat m_img;
	};
}
 
#endif
实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/*************************************************************************
	> File Name: water.cpp
	> Author: Dong ZhiChao
	> Mail: 
	> Created Time: Sun 04 Dec 2016 01:34:38 PM CST
 ************************************************************************/
#include 
#include "water.h"
 
using namespace std;
using namespace mars;
 
water_print::water_print(string &file_in)
{
	m_img = imread(file_in);
	if(m_img.empty()){
		cerr << "read image file error!";
	}
}
 
water_print::~water_print()
{
	; // do nothing
}
 
int water_print::text(string &logo_str, int x, int y)
{
	putText(m_img, logo_str, Point(x, y), 
		CV_FONT_HERSHEY_COMPLEX, 1, cvScalar(200, 200, 200, 0));
 
	return 0;
}
 
int water_print::image(string &logo_img, double alpha)
{
	Mat logo    = imread(logo_img);
	int x, y;
 
	x = m_img.cols - logo.cols - 10;
	if(x < 0)
		x = 10;
	y = m_img.rows - logo.rows - 10;
	if(y < 0)
		y = 10;
 
	Mat img_roi = m_img(Rect(x, y, logo.cols, logo.rows));
 
	if(logo.empty()){
		cerr << "read logo image error!";
		return -1;
	}
 
	addWeighted(img_roi, alpha, logo, 1.0-alpha, 0, img_roi);
 
	return 0;
}
 
int water_print::show(void)
{
	imshow(SHOW_WINDOW_NAME, m_img);
	waitKey();
 
	return 0;
}
 
int water_print::write(string &file_out)
{
	imwrite(file_out, m_img);
 
	return 0;
}
主文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*************************************************************************
	> File Name: t_main.cpp
	> Author: Dong ZhiChao
	> Mail: 
	> Created Time: Sun 04 Dec 2016 01:46:19 PM CST
 ************************************************************************/
#include "water.h"
 
using namespace mars;
 
int main(int argc, char* argv[])
{
	if(argc < 3){
		cout << "Usage: waterp [source file] [target file]" << endl;
		return 1;
	}
	water_print* p_waterp;
	string img_file(argv[1]);
	string file_out(argv[2]);
	try{
		string logo_str("www.dongzhichao.com");
		string logo_img("./logo.png");
		p_waterp = new water_print(img_file);
		p_waterp->text(logo_str, 10, 20);
		p_waterp->image(logo_img, 0.5);
		//p_waterp->show(); // for test
		p_waterp->write(file_out);
	}catch(...){
		cerr << "catch some error!";
	}
	delete p_waterp;
 
	return 0;
}
工程使用CMake进行编译,CMakeLists.txt文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
cmake_minimum_required(VERSION 2.8)
 
PROJECT(waterp) 
 
FIND_PACKAGE(OpenCV REQUIRED)    
 
INCLUDE_DIRECTORIES(
	${ShowImage_SOURCE_DIR} 
) 
 
ADD_EXECUTABLE(waterp water.cpp t_main.cpp)  
TARGET_LINK_LIBRARIES (waterp ${OpenCV_LIBS} )
最后在目录下创建两个文件夹:images和dest,写个简单的shell脚本用于批量添加水印:
1
2
3
4
5
6
7
#!/bin/bash
 
filelist=`ls ./images`
for file in $filelist
do 
	./waterp "./images/""$file" "./dest/""$file"
done
这样以后只要把想要增加水印的图片放到images文件夹下,然后运行一下脚本就可以完成批量添加水印的工作,是不是很简单呢?
结尾附上图像处理经典图片lena的水印版
 lena

发表评论