目录
>>字符画效果展示 >>字符视频效果展示 >>Python代码展示 >>思考过程
>>>字符画效果展示
(注:字符图画放大食用 效果更佳)

秦时明月 >>> 娥皇/女英

秦时明月 >>> 字符画的娥皇/女英

秦时明月 >>> 年轻的小庄

秦时明月 >>> 年轻的字符画小庄

功夫熊猫 >>> 一只可爱的小熊猫

功夫熊猫 >>> 一只字符画的小熊猫
>>>字符视频效果展示
作者将视频的每一帧图片转化为字符画,在利用Pymovie组合为视频。
视频为BILIBILI鬼畜视频:《念诗之王》——K-DA女团
站外链接为BILIBILI视频URL地址
00:00
>>>代码展示
import cv2, random
import numpy as np
def get_str_pic(frame, K=5):
"""
思 路:
--------------
利用 聚类 将像素信息聚为3或5类,颜色最深的一类用数字密集地表明,阴影的一类用
“-”横杠表明,明亮部分空白表明。
参 数:
--------------
frame
需要传入的图片信息。可以是opencv的cv2.imread()得到的数组,也可以是
Pillow的Image.read()。
K
聚类数量,推荐的K为3或5。根据经验,3或5时可以较为优秀地处理许多图像了。
若默认的K=5无法很好地表现原图,请修改为3进行尝试。若依然无法很好地表现
原图,请换图尝试。 ( -_-|| )
注 意:
--------------
聚类数目理论可以取大于等于3的任意整数。但水平有限,无法自动判断当生成的
字符图像可以更好地表现原图细节时,“黑暗”、“阴影”、”明亮“之间边界在哪。
所以说由于无法有效利用更大的聚类数量,那么便先简单地限制聚类数目为3和5。
"""
if type(frame) != np.ndarray:
frame = np.array(frame)
#if K != 3 and K != 5:
#raise Exception("K需要是3或者5")
height = frame.shape[0]
width = frame.shape[1]
frame_gray = cv2.cvtColor(frame,
cv2.COLOR_BGR2GRAY)
frame_array = frame_gray.reshape((-1,1))
frame_array = np.float32(frame_array)
# 设置相关参数。
criteria = (cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,
10, 1.0)
flags = cv2.KMEANS_RANDOM_CENTERS
# 得到labels(类别)、centroids(矩心)。
# 如第一行6个像素labels=[0,2,2,1,2,0],则意味着6个像素分别对应着
# 第1个矩心、第3个矩心、第3、2、3、1个矩心。
compactness, labels, centroids = cv2.kmeans(frame_array, K,
None, criteria, 10, flags)
centroids = np.uint8(centroids)
# labels的数个矩心以随机顺序排列,所以需要简单处理矩心.
centroids = centroids.flatten()
centroids_sorted = sorted(centroids)
# 获得不同centroids的明暗程度,0最暗
centroids_index = np.array([centroids_sorted.index(value)
for value in centroids])
up = [abs((3*i-2*K)/(3*K)) for i in range(1, 1+K)]
upper_bound = up.index(np.min(up))
low = [abs((3*i-K)/(3*K)) for i in range(1, 1+K)]
lower_bound = low.index(np.min(low))
labels = labels.flatten()
# 将labels转变为实际的明暗程度列表,0最暗。
labels = centroids_index[labels]
# 列表解析,每2*2个像素挑选出一个,组成(height*width*灰)数组。
labels_picked = [labels[rows*width:(rows+1)*width:2]
for rows in range(0,height,2)]
canvas = np.zeros((3 * height, 3 * width, 3), np.uint8)
canvas.fill(255) # 创建长传为原图三倍的白色画布。
# 由于 字体大小为0.45时,每个数字占6*6个像素,而白底画布为原图三倍
# 所以 需要原图中每2*2个像素中挑取一个,在白底画布中由6*6像素大小的数字表明这个像素信息。
y = 8
for rows in labels_picked:
x = 0
for cols in rows:
if cols <= lower_bound:
cv2.putText(canvas, str(random.randint(2,9)),
(x, y), cv2.FONT_HERSHEY_PLAIN, 0.45, 1)
elif cols <= upper_bound:
cv2.putText(canvas, "-", (x, y),
cv2.FONT_HERSHEY_PLAIN, 0.4, 0, 1)
x += 6
y += 6
return canvas
if __name__ == '__main__':
img = cv2.imread("2018???138_yexQ8.jpeg")
# 若字符图像结果不好,可以尝试更改K为3。
# 若依然无法很好地表现原图,请换图尝试。 -_-||
str_pic = get_str_pic(img, K=5)
cv2.imwrite("result.jpg", str_pic)
>>>思考过程
如今网上关于如何实现字符图像多为一种方法,那就是
1、构造一行从密集到稀疏的”符号”集合(列如:$@B%8&WM#*oahkbdpqw mZO0QLCJUYXzcvunxrjft/|()1{}[]?-_ +~<>i!lI;:,”^`'. )。
2、图像转为灰度(像素信息便只有一个数值),像素值范围为 0~255 的整数,共256个值。于是计算(像素值 / 256)得出一个像素值在像素范围中的相对位置,也就是百分比值。
3、最终在“符号“集合中找到一样相对位置(百分比值)的那个符号,输出它。此为网上大部分的实现方法,但是通过观看结果(图像或者视频)展示发现,此方法存在一些问题,如轮廓比较模糊,在图片整体为暗色调时,整个字符图像会变成一团浆糊。
4、给予以上发现,作者尝试使用新的方法实现字符图像。在辅修学位论文阶段偶然发现同学使用过“聚类”手段,可以根据不同城市的某个经济数据达到城市特征归类的目的。于是开始思考是否可以借鉴聚类手段,根据像素数值大小的特征,将它们分为不同种类,最暗的部分使用较为密集的“数字”表明,次暗的阴影部分使用 “-” 横杠表明, 明亮部分可以使用 “.” 点号或者空白表明。
5、不足:第一只是机械地设定聚类数量为3或5,其次只是机械地将“黑暗、阴影、明亮”的分界设定为(最终类别 / 聚类数量)中最靠近0.33和0.66的两个分界点。缘由在于无法通过程序评估不同“黑暗、阴影、明亮”的分界设定下最终字符图像的优劣程度,也就是无法有效利用更多个聚类类别数量。于是乎,作者暂时限定聚类数量和分界设置。但是相较于网上已经流传的方法来讲,应该可以认为效果有了较大的提升。
点个赞吧,么么哒





图片转字符画,封装一下可以上传github给别人用了