一个简单的蒙特图(彩色文字组成图形)生成器,使用 Python3 编写(需要 lambda
特性简化语法)
使用了 Pillow、Numpy 来进行矩阵运算(诸如互相关)、瞎 pip3 list
找的 joblib
来执行矩阵(或序列集)运算的并行化
灵感来自另一个 简易文字蒙太奇
至于为什么又起名叫做蒙特图,只是个名字而已……
- 读取输入图像,预处理
- 分配输出图像,其大小默认是预处理图像的大小
- 根据文本排版布局开始,文本流意味着每次使用不同的文本开始排字(这样可以实现 N 字一色而不是单个
-txt
只有一种颜色、必须等重复)- 区块意味着
(文字X, 文字Y)-(文字X+Width+textspH*2, 文字Y+Height+textspV*2)
,即文字覆盖区域 (公式已经作为lambda
参数方便修改) - 取文本后的图像色区块计算其平均色和背景flag
- 如果是背景,不要生成文字
- 否则画上(自动变色)的文字
- 默认情况下,
foreach x. y=...
一行的文字排版会作为一个 job 并行计算
- 区块意味着
- 重复处理直到表示完整个图像
-
要嵌入的文本:
textBlend
.--text, -txt
-
把文本视作文本流:
textIsSequence
.--text-isseq, -tseq!
(以,
切分,实际上后端 sep 是参数,前端不是)(啊实际上是直接在前端切分…… 后端看情况是从流取还是repeat(x,n)
) -
指定文本颜色而不使用自动根据背景变换:
textColor
.--text-color, -txtcr
(在仅仅只用此工具生成辅助图像处理的图层时有用) -
生成图像的字体(大小):
font, fontSize
.--font, -fnt
.--font-size, -fntsz
-
文本文字间隔/行间距:
spaceTextH, spaceTextV
.--text-space, -textsp H V
-
源图像左右/上下生成间隔:
paddingLR, paddingUB
.--padding, -pad LR UB
-
分配生成图像的大小:
outputScale
.--draw-size, -size
-
支持蒙特自动(当然是按照文字布局)裁切图像,主体(不同于背景色的部分)使用相应文字表示:
backgroundColor
.--background-color, -bgcolor
背景色,默认#FFFFFF
backgroundCrDiffMax
.--background-diffmax, -bgdiffLT
最大限度的色值『不同』程度,默认#0a0a0a
backgroundCrDiffAlgor
.--background-diffalg, -bgdiff
色差算法,可以选择lum
(低精度灰度) 或identity
(直接求差)。结果会通过abs(...)
textTintThres
.--text-tint-thres, -bgtintGT% N
当请求色区拥有 N% 个(模糊)背景色块时,跳过此块生成(tintGT/100)>(bgpxs/blkpxs)
-
蒙特还允许使用变换矩阵进行『互相关』预处理(就是 GIMP 的蒙板),以方便特殊图像的处理需求
preprocessMatrix
.--kernel, -kern
在开始求当前色区块的主体色/填充flag 之前,可以先利用矩阵进行互相关变换,矩阵作为Image
递交preprocessMatrixIsDiffOnly
.--kernel-diffonly, -kern-diff
如果为真,则只在判断是否显字时使用互相关运算,求色区块平均色则不使用
-
同时构造并且输出渲染文本的结构(文字+换行序列,包括背景色的部分,可以用来生成 ASCII Art),这个部分由前端辅助
emitFrameSource
,-emit-frame fmt
以下是无关生成算法的参数
- 输入图像们:
image [image ...]
- 图像编码格式:
saveFormat
.--output-format, -oe
- 输入/输出的色彩模式:
colorspace, colorspaceInput
.--convert, -ic
,--output-crspace, -oc
- 并行处理任务数:
--jobs, -j
- 安静模式:
--quiet, -q!
- 批处理的输出文件名格式字符串:
ofnameFmtstr
.--output-fmtstr, -ofstr
- 生成图像将被缩放到的大小:
postScale
.--post-scale, -size-o
(这个版本很简陋,不支持自动计算文字间距) - 直接与原图像进行 Alpha 混合成:
--alpha-composite, -ac
混成方式是(蒙特图 >> 原图)
- 直接与原图像进行混合:
--blend, -bl mon_alpha
混合方法是(原图*(100%-mon_alpha) + 蒙特图*mon_alpha)
- 直接预览,不要输出(使用命令行):
--preview cmd
- 直接预览,不要输出:
-view!
因为透明度参数对蒙特图生成的影响实在是太小了,而且同时支持不同的 (r,g,b,???,...)
元组长度比较困难,所以暂时不打算支持
不过只是不保证可以用,Pillow 还是有一定兼容性的,可以利用 --convert
参数覆盖默认的 RGB
颜色系统
输出的 Alpha 由于默认 --colorspace
为 ARGB
,不存在此问题
SimpleTextMontage 使用了 fontSize*ratio
(输出画布和 font 都乘比例)的方式保证文本和画布比例相同,调整文本像素即可变动容字数
不过 MontagePY 没有使用,而是分离了输出画布和文字大小参数,这样很大的比例输出图也可以放许多很小的字(当然是说配置上方便一些,等价的)。
如果要使用自动配置的 fontSize
, outputScale
, spaceText H/V
,提供 --draw-ratio, -size%
即可,不过它会被单独的配置覆盖。
参数 --start-service, -serv
使用 --accept-filename
, --accept-listing, -serv-list
来支持使用文件名(而不是 fileno)/文件列表
Montage.py 支持使用 HTTP 接口访问来生成文本,并且接口和参数风格遵循一般的 Web 应用规范
利用 HTTP API,可以编写生成图片的 HTML 单/多页应用
生成接口暂时只提供解析执行命令行:
-
GET /cmd?(command args)
,POST /gen?args=(command args)
-
GET /fileno/(fileno)
拿指定 fd 的图像 -
DELETE /fileno/(fileno)
删除指定 fd 的图像 -
GET /file/(name)
拿指定名字的图像 -
DELETE /file/(name)
删除指定名字的图像
不存在 OPTIONS
返回为 Json array [ { name, fileno }, ... ]
没错,暂时只能做单机……
from PIL import Image
from PIL import ImageDraw, ImageFont
from PIL import ImageChop
from joblib import Parallel
from joblib import delayed as makeJob
import numpy as np
from numpy import arange, asarray
(ImageFont.truetype, ImageFont.FreeTypeFont, ImageFont.load_path) # Fonts
(Image.width, Image.height) # Box2D
(Image.getpixel, Image.putpixel) # Color I/0
Image.resize # average resample???
(Image.open, Image.new) # Create
(Image.save, Image.show, Image.close) # Post ops
(Image.crop, Image.offset) # Crop
Image.alpha_composite # Composite with original image
Image.blend # Image blend
(arange, asarray, Image.fromarray) # Matrix processing
(Parallel, makeJob) # Parallel computing
(Image.seek) # Video processing???
(Image.getbbox) # For pre-processing blackbox???
Image.histogram # Use histogram-diff method???
MontagePY 的排版复杂很多(单位的长度可能是不确定的),也不能在循环外直接计算 cols, rows
大致可以,但实际上,只有文本不是序列或序列长度一致 --text-chars!, -tchars!
的时候才能使用优化版公式
global spaceTextH, spaceTextV
global paddingLR, paddingUB
global font
global currentText
global selectionW, selectionH
def idiv(b, a): return int(b / a)
textW, textH = font.getsize(currentText)
unitW, unitH = int(textW+ spaceTextH), int(textH+ spaceTextV)
cols, rows = (idiv(selectionW, unitW), idiv(selectionH, unitH))
gapLR, gapUB = (selectionW- (cols*unitw)), (selectionH- (rows*unith))
padLR, padUB = idiv(gapLR+spaceTextH,2), idiv(gapUB+spaceTextV,2)
然后可以如此循环来执行计算
for ty in range(padUB, selectionH-padUB, unitH):
for tx in range(padLR, selectionW-padLR, unitW):
txt = next(text_seq)
txtregion = toImageRegion(range(ty, ty+unitH), range(tx, tx+unitW))
draw, backtint = colorAverage(txtregion)
if not draw: continue
color = textColor or backtint
draw.text((tx,ty), txt, font=font, fill=color)
与隔壁的算法大致上等价(为了可读性重写了部分)
usePreconfColor = len(textColor) > 0
for n in range(0, secRows):
textx = leftPadding
for m in range(0, secCols):
tempsec = ims.crop((textx, texty, textx + secWidth, texty + secHeight))
(ac, flg) = getAvgColor(tempsec)
avgColor = RGB2hexColor(ac[0], ac[1], ac[2]) # 其实这里可以删掉,fill 不一定得是字符串
#if flg is True and textp < len(text): # 这里 and 的第二个表达式可以删除
if not flg: continue
dr.text((textx, texty), text[textp],
font=font, fill=textColor if usePreconfColor else avgColor)
textp = (textp+1) % textp # 修改,也可改为 `max(lastIdx, textp)`?
textx += secWidth #col
texty += secHeight #row
懒得写,WTFPL
有点良心是不会瞎抄的、 没有良心是不会用其他语言和库改写算法的
虽然说了这么多,可其实现的只是一点点,连 CLI 都没有
只是因为太懒而且没时间的缘故,不要想太多,不喜也别喷(逃)😂
import ImgConvolve
q=ImgConvolve.open('/home/DuangSUSE/Projects/Share/Others/avatar.webp')
p=ImgConvolve.open('/home/DuangSUSE/Projects/Share/Others/avatar.jpg')
r=p.convolve(q.img)
r.show()
p.filter_convolve([[1, 0, -0.2], [1, 0, -1], [0.1, 0, -1.8]]).show()
什么的,当然最重要的是我要拿它去算卷积…… 虽然很慢,大概只能对少部分大图像使用