watermarker.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. import argparse
  4. import os
  5. import sys
  6. import math
  7. import textwrap
  8. from PIL import Image, ImageFont, ImageDraw, ImageEnhance, ImageChops, ImageOps
  9. def add_mark(imagePath, mark, args):
  10. '''
  11. 添加水印,然后保存图片
  12. '''
  13. im = Image.open(imagePath)
  14. im = ImageOps.exif_transpose(im)
  15. image = mark(im)
  16. name = os.path.basename(imagePath)
  17. if image:
  18. if not os.path.exists(args.out):
  19. os.mkdir(args.out)
  20. new_name = os.path.join(args.out, name)
  21. if os.path.splitext(new_name)[1] != '.png':
  22. image = image.convert('RGB')
  23. image.save(new_name, quality=args.quality)
  24. print(name + " Success.")
  25. else:
  26. print(name + " Failed.")
  27. def set_opacity(im, opacity):
  28. '''
  29. 设置水印透明度
  30. '''
  31. assert opacity >= 0 and opacity <= 1
  32. alpha = im.split()[3]
  33. alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
  34. im.putalpha(alpha)
  35. return im
  36. def crop_image(im):
  37. '''裁剪图片边缘空白'''
  38. bg = Image.new(mode='RGBA', size=im.size)
  39. diff = ImageChops.difference(im, bg)
  40. del bg
  41. bbox = diff.getbbox()
  42. if bbox:
  43. return im.crop(bbox)
  44. return im
  45. def gen_mark(args):
  46. '''
  47. 生成mark图片,返回添加水印的函数
  48. '''
  49. # 字体宽度、高度
  50. is_height_crop_float = '.' in args.font_height_crop # not good but work
  51. width = len(args.mark) * args.size
  52. if is_height_crop_float:
  53. height = round(args.size * float(args.font_height_crop))
  54. else:
  55. height = int(args.font_height_crop)
  56. # 创建水印图片(宽度、高度)
  57. mark = Image.new(mode='RGBA', size=(width, height))
  58. # 生成文字
  59. draw_table = ImageDraw.Draw(im=mark)
  60. draw_table.text(xy=(0, 0),
  61. text=args.mark,
  62. fill=args.color,
  63. font=ImageFont.truetype(args.font_family,
  64. size=args.size))
  65. del draw_table
  66. # 裁剪空白
  67. mark = crop_image(mark)
  68. # 透明度
  69. set_opacity(mark, args.opacity)
  70. def mark_im(im):
  71. ''' 在im图片上添加水印 im为打开的原图'''
  72. # 计算斜边长度
  73. c = int(math.sqrt(im.size[0] * im.size[0] + im.size[1] * im.size[1]))
  74. # 以斜边长度为宽高创建大图(旋转后大图才足以覆盖原图)
  75. mark2 = Image.new(mode='RGBA', size=(c, c))
  76. # 在大图上生成水印文字,此处mark为上面生成的水印图片
  77. y, idx = 0, 0
  78. while y < c:
  79. # 制造x坐标错位
  80. x = -int((mark.size[0] + args.space) * 0.5 * idx)
  81. idx = (idx + 1) % 2
  82. while x < c:
  83. # 在该位置粘贴mark水印图片
  84. mark2.paste(mark, (x, y))
  85. x = x + mark.size[0] + args.space
  86. y = y + mark.size[1] + args.space
  87. # 将大图旋转一定角度
  88. mark2 = mark2.rotate(args.angle)
  89. # 在原图上添加大图水印
  90. if im.mode != 'RGBA':
  91. im = im.convert('RGBA')
  92. im.paste(mark2, # 大图
  93. (int((im.size[0] - c) / 2), int((im.size[1] - c) / 2)), # 坐标
  94. mask=mark2.split()[3])
  95. del mark2
  96. return im
  97. return mark_im
  98. def main():
  99. parse = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
  100. parse.add_argument("-f", "--file", type=str,
  101. help="image file path or directory")
  102. parse.add_argument("-m", "--mark", type=str, help="watermark content")
  103. parse.add_argument("-o", "--out", default="./output",
  104. help="image output directory, default is ./output")
  105. parse.add_argument("-c", "--color", default="#808080", type=str,
  106. help="text color like '#000000', default is #808080")
  107. parse.add_argument("-s", "--space", default=100, type=int,
  108. help="space between watermarks, default is 75")
  109. parse.add_argument("-a", "--angle", default=30, type=int,
  110. help="rotate angle of watermarks, default is 30")
  111. parse.add_argument("--font-family", default="SimHei.ttf", type=str,
  112. help=textwrap.dedent('''\
  113. font family of text, default is './font/SimHei.ttf'
  114. using font in system just by font file name
  115. for example 'PingFang.ttc', which is default installed on macOS
  116. '''))
  117. parse.add_argument("--font-height-crop", default="2.2", type=str,
  118. help=textwrap.dedent('''\
  119. change watermark font height crop
  120. float will be parsed to factor; int will be parsed to value
  121. default is '2.2', meaning 2.2 times font size
  122. this useful with CJK font, because line height may be higher than size
  123. '''))
  124. parse.add_argument("--size", default=50, type=int,
  125. help="font size of text, default is 50")
  126. parse.add_argument("--opacity", default=0.15, type=float,
  127. help="opacity of watermarks, default is 0.15")
  128. parse.add_argument("--quality", default=80, type=int,
  129. help="quality of output images, default is 80")
  130. args = parse.parse_args()
  131. if isinstance(args.mark, str) and sys.version_info[0] < 3:
  132. args.mark = args.mark.decode("utf-8")
  133. mark = gen_mark(args)
  134. if os.path.isdir(args.file):
  135. names = os.listdir(args.file)
  136. for name in names:
  137. image_file = os.path.join(args.file, name)
  138. add_mark(image_file, mark, args)
  139. else:
  140. add_mark(args.file, mark, args)
  141. if __name__ == '__main__':
  142. main()