今天来分享一个自己做的实用工具,那就是为图片添加水印。最大的特点就是支持文字水印(可以为汉字,英文),也支持图片水印。既可以单张图片加水印,也可以支持批量图片加水印。

诱因

我本人平时喜欢写点博客什么的,所以不可避免的会使用贴图,然而目前(2016年11月27日)而言CSDN上还不能有效的为自己的图片自动的添加水印。所以这给那些爬取博客作为自己网站博客的网站提供了可乘之机。

比如: 自己在CSDN上的原创,却被其他占据了头条。
被人盗用

先不说自己的文章质量怎么样,无论好与坏,都是自己一个字母一个字母敲出来的,多少也有点苦劳。但是被别人不加声明的就弄走了,这就让人有点幽怨了。更有甚者,还会加上他们自己网站的水印,“成了他们原创的了”。··· ···

于是,为了给自己一个交代。决定写个小工具。自己给自己的图片添加水印,自己保护自己吧。

原理

图片水印

对图片水印而言,我们就可以这样理解:把一张图片粘贴到另一张图片上。只不过这是使用代码实现的罢了。

文字水印

文字水印稍微有点不太一样。但是原理上其实也是差不多的。那就是先使用PIL生成一张背景图,然后再这张图片上放上一些文字(对于汉字需要额外的做些处理)。最后使用图片水印的原理把这张生成的图片作为水印添加到另一张图片上。

完整代码

下面贴出完整代码,有需要的请自取。

# coding:utf-8
import sys

reload(sys)
sys.setdefaultencoding('utf8')
#    __author__ = '郭 璞'
#    __date__ = '2016/11/26'
#    __Desc__ = 单个,批量给图片添加水印,既可以使用文字水印,\
# 也可以使用图片水印(默认拥有更高的优先级,如果既指定文字,又指定图片,默认仅采用图片水印)

# 使用PIL操作图片
from PIL import Image, ImageFont, ImageDraw
# 获取路径下所有图片
import os
# 添加命令行参数
import argparse

# 使用水印图片作为原材料添加水印,需指定完整路径信息
def picmark(tomarkpath, markpath):
    image = Image.open(tomarkpath)
    mark = Image.open(markpath)
    layer = Image.new("RGBA", image.size, (0, 0, 0, 0))
    x, y = image.size[0]-mark.size[0]-12, image.size[1]-mark.size[1]-10
    layer.paste(mark, (x, y))
    out = Image.composite(layer, image, layer)
    out.save(tomarkpath)
    print "{} has beed added the watermark successfully :)".format(tomarkpath)

# 生成图片水印素材,以供使用。
def textgenerate(text='http://blog.csdn.net\nCSDN 郭 璞', textcolor=(0, 0, 0), bgcolor=(255, 255, 255)):
    # After test, those parameters is suitful for Chinese watermark.
    size = len(text) * 12, len(text) * 2
    fontsize = len(text) / 2  if len(text)/2 > 20 else 24

    font = ImageFont.truetype('simsun.ttc', fontsize)
    image = Image.new('RGB', size, bgcolor)
    draw = ImageDraw.Draw(image)
    draw.text((size[0] / 7, size[1] / 5), u'{}'.format(text), textcolor, font=font)
    # image.save(r'./asdsadsadsasa.png')
    return image

# 生成文字水印结果,需指定完整路径,第二个参数为生成的文字水印素材,地位相当于一个外部的水印图片。
def textmark(tomarkpath, markimage):
    image = Image.open(tomarkpath)
    layer = Image.new("RGBA", image.size, (0, 0, 0, 0))
    x, y = image.size[0] - markimage.size[0] - 12, image.size[1] - markimage.size[1] - 10
    layer.paste(markimage, (x, y))
    out = Image.composite(layer, image, layer)
    out.save(tomarkpath)
    print "{} has beed added the watermark successfully :)".format(tomarkpath)

# 获取指定路径下的所有图片,包括png,jpg, jpeg, gif等。可手动拓展。
def getallpics(dirpath):
    pics = []
    files = os.listdir(dirpath)
    for filename in files:
        if str(filename).endswith('jpeg') or str(filename).endswith('png') or str(filename).endswith('jpg') or str(filename).endswith('gif'):
            pics.append(filename)
        else:
            continue
    return pics

# 批量添加图片水印,默认添加在右下角位置
def runbatchlywithmark(markpath, dirpath):
    tomarkfiles = getallpics(dirpath=dirpath)
    watermarkname = markpath.split('/')[-1]
    tomarkfiles.remove(watermarkname)
    # print tomarkfiles
    for item in tomarkfiles:
        picmark(item, markpath)
    print "All done!"

# 批量添加文字水印,默认添加在图片右下角位置。会根据需求自动计算出合适大小的文字水印图片
def runbatchlywithtext(dirpath, text, textcolor=(85, 123, 205), bgcolor=(0,0,0)):
    tomarkfiles = getallpics(dirpath=dirpath)
    # print tomarkfiles
    markimage = textgenerate(text=text, textcolor=textcolor, bgcolor=bgcolor)
    for item in tomarkfiles:
        textmark(item, markimage)
    print "All done!"


# 主函数,完成所有业务逻辑处理
if __name__ == '__main__':

    # 声明相关命令行参数信息
    parser = argparse.ArgumentParser(description='Add watermark to your pictures batchly and singly!\n You can also assign the text color or background color.\n')
    parser.add_argument('-t', '--text', type=str, default='CSDN  郭 璞', help='the text you want to attach to the picture!')
    parser.add_argument('-tc', '--textcolor', type=str, default=(85, 123, 205), help='the text color you want!\n Both style like (85, 123, 205) and #XXXXXX can be all right')
    parser.add_argument('-bgc', '--bgcolor', type=str, default=(32, 234, 105),
                        help='the background color you want!\n Both style like (32, 234, 105) and #XXXXXX can be all right')
    parser.add_argument('-dp', '--dirpath', type=str, default=r'./', help='the directory contains pictures you want to attach watermark.\n Default directory is current directory.')
    parser.add_argument('-mp', '--markpath', type=str, help='the watermark picture you prepared already!\n Generally it\'s smaller than raw pictures.')
    parser.add_argument('-spp', '--singlepicpath', type=str, help='the single picture path you want to add watermark.')
    args = parser.parse_args()

    # Because of the codec in cmd on windows, utf-8 should be use carefully when attach Chinese text as watermark.
    text = args.text.decode('gbk').encode('utf-8')
    textcolor = args.textcolor
    bgcolor = args.bgcolor
    dirpath = args.dirpath
    markpath = args.markpath
    singlepicpath = args.singlepicpath

    # for single picture attaching watermark.
    if singlepicpath:
        # attach text or using mark picture prepared
        if markpath:
            picmark(tomarkpath=singlepicpath, markpath=markpath)
        else:
            markimage = textgenerate(text=text, textcolor=textcolor, bgcolor=bgcolor)
            textmark(tomarkpath=singlepicpath, markimage=markimage)
    else:
        # attach text or using mark picture prepared
        if markpath:
            runbatchlywithmark(markpath=markpath, dirpath=dirpath)
        else:
            runbatchlywithtext(dirpath=dirpath, text=text, textcolor=textcolor, bgcolor=bgcolor)

使用方法

由于使用了argparse,所以可以很方便的进行命令行参数的使用。如果有什么不懂,直接使用help命令即可。会有详细的使用方式输出。

help命令

help命令查看使用方法

单张图片水印

要添加图片水印,就需要事先准备好一张效果图了。比如博主准备了一张280X52像素大小的素材。
水印素材

那么为了演示方便,还得找一张要进行水印的图片才行。不妨使用下面的这张图片好了。
待加水印原图

那么正式开始咯。
单张图片水印

单张文字水印

好了,还是那张图片,这次尝试一下文字水印。
除了可以指定-t -tc, -bgc等选项。我们还可以使用默认值,虽然默认值是博主自己的信息,(^__^) 嘻嘻……

单张文字水印

批量图片水印

批量的其实就是把指定目录下的所有的要进行水印的图片添加了水印而已。
批量图片水印命令

然后在测试的文件夹下可以查看到,所有的图片(除了水印图片自身)都被添加了图片水印。
批量图片水印效果

批量文字水印

同理,下面将演示批量添加文字水印。
批量添加文字水印效果

相信图片右下角的绿色的标示已经很明显了吧。

注意与拓展

好了,说完了让人激动的条目。下面可以来聊聊这个工具存在的一些问题吧。

注意

编码问题

首先需要注意的仍然是老生常谈的Python中的编码问题了。默认Python脚本文件以UTF-8编码,而Windows的CMD命令行为GBK编码,所以输入汉字的时候Python解释器就会报错。解决的办法目前是采用硬编码的方式。

text = args.text.decode(‘gbk’).encode(‘utf-8’)

源码中有这么一行命令,就是为了将在CMD中输入的汉字转为UTF-8编码,让Python解释器可以正确的运行。

但是如果您的环境是Linux,那就不能这样写了。否则还是会报出解码异常问题的。
所以就需要您自己查看一下自己的环境的编码信息,然后对脚本进行硬编码处理。

或许,这个时候会有许多不服气的朋友要鼓吹Python3多么多么好了,其实我也不反对,但是个人觉得说这些,没有什么意义。语言是工具,每个人都可以有自己的喜好,没有谁非得按照你的意愿办事,管好自己就行了。题外话了,不多说了。

文字水印自适应

脚本中对于文字水印做了一点自适应的处理。但是还不够完善。有需要的话可以自行指定。

另外,文字水印字体大小同样可以进行处理。

拓展

我觉得除了针对上面的问题进行优化之外,还有一个比较大,也比较容易实现的方向。那就是水印位置。脚本中简单的按照博主自己的需求手动的指定为图片右下角了。这一点不是很有普适性。因此可以进一步对脚本进行拓展,以实现人以为会水印的添加。

总结

回顾一下,本文主要是介绍了博主从为什么要制作这样的一个工具出发,到一步步的实现,到使用方法以及后续拓展等。

回到起点,虽然国内的版权意识相比较而言还是比较淡薄。但已经是进步很大了,而且这也只是个时间问题。

相信,CSDN会把这项工作做得越来越好的。


本文转载:CSDN博客