由于最近需要向博客里传图片,传之前需要打水印,图片一多打水印工作就变得很麻烦,考虑到未来的日子里还有无穷无尽的图片需要加水印,即使在这个图片处理软件遍地都是的时代,鼠标点来点去也是一件很头痛的事情,所以干脆写个程序一次性解决图片加水印的问题。
图像处理自然就需要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的水印版