夕日は強く、少しでも眩しく、ガラス越しにキーボードを照らしていました。
これは普通のプラスチック製のキーボードです。隙間はほこりや皮脂でいっぱいです。強い光の照明の下では、顕微鏡での視野のように、よりはっきりと見えます。
長年働いてきた私は、キーボードは単なる道具であり、技術レベルに影響を与えるのは上司からの要求だけだということを長い間理解してきました。
IDEのカーソルはそこで点滅していて、1つのスペースを移動することはありませんでしたが、私の現在の考えのように、静止することはありませんでした。
朝のシーンは私の心の中で繰り返され続けました:
上司は言った:あなたはOCR技術が今非常に成熟していると言いませんでしたか?それから私たちは自分たちでそれをします!
私は言った:フレームワークは非常に成熟している。しかし、私たちはデータを持っていません...
上司は尋ねました:あなたはどんなデータが欲しいですか?
私の答え:マシンに「1」を認識させたい場合は、少なくとも500枚の「1」の写真を使用してマシンをトレーニングする必要があります。
上司は尋ねました:「2」のトレーニングはどうですか?
私の答え:「2」の写真500枚、繰り返しはできません。繰り返しは1枚の写真としてカウントされます。考えてみてください。一般的に使われている漢字は3,000以上あり、資料はありません。オンラインで購入しますか...
上司は深く考え、突然彼の眼鏡が点滅しました。ねえ、プログラマーを出させれば、新しい妻と一緒に出てくることさえできます。
私はすぐに説明しました:それが目的です。
手元に誰もいない、ただのインターンだと言いたかっただけです。
上司の電話が突然鳴り、彼は電話を覆って私に言った:私は出張に行く。3日後、私が戻ってきたら、あなたは私の妻を与えなければならない...いいえ、データセットを新しいものに与える!!
1日目:黒い枠を描き、単語を入力します
早朝、豆乳を持ってオフィスに入った。
私のオフィスは大きくはありません。ワークステーションは2つしかなく、1つは私用、もう1つはインターンのXiaoWang用です。しかし、ドアの看板には「工業団地ソフトウェア研究開発センター」とはっきりと書かれており、上司は将来的には200人の技術チームに拡大すると述べた。
Xiao Wangは現在、彼の4年生であり、今年はここでインターンを務めています。役職は研究開発エンジニアです。シャオ・ワンはとても真剣に働いています。彼は毎日私より早く来ます。彼がよく訓練されていれば、彼は良い苗になるはずです。
「すみません、これは財務部門ですか?」おじは頭をドアから突き出して尋ねました。
"番号!"。
「いや、いや、どうしてそんなに騒がしいんだ!あなたの部屋は財務部門にとても似ている。キャビンには二人いる。あなたは会計士で、彼はレジ係だ。」
私はシャオ・ワンに歩いて行きました。シャオ・ワンはナゲッツのブログを読んでいて、そこには多くの偉大な神々がいました:TFボーイ、チュン兄弟、リン・サンシン…。
私が「工業団地ソフトウェア研究開発センター」の責任者であるため、シャオ・ワンが私を上司と呼んでいます。私が彼を担当し、彼が私を担当しています。
跟你安排一个任务,很简单,用python先画一个32*32像素的黑色背景,然后在上面写上白色的字,你去写吧。
小王很快就写好了。
from PIL import Image
from PIL import ImageDraw
# 画出一个32*32的黑色框
img = Image.new("RGB", (32, 32), "black")
# 在黑框里写上字
draw = ImageDraw.Draw(img)
draw.text((0,0), "2", (255, 255, 255))
# 保存画好的图片
img.save("2.png")
小王上午就来找我汇报工作。
我试过了在黑色背景图上写上字母、数字、符号,都是可以的。
我连连点头,称赞小王很棒。
小王问,接下来要做什么。我说,明天再告诉你。
第二天:先加载字体,再绘制字符
第二天,小王问我,老大,今天什么任务啊,我昨天等了一下午。
我说,运行你昨天的代码,输出个汉字试试。
小王执行了下面的代码: draw.text((0,0), "汉", (255, 255, 255))
,结果报错了:AttributeError: 'ImageFont' object has no attribute 'getmask2'
。
小王愣在一旁,我却微微一笑:
你没有加载支持汉字的字体,就直接绘制汉字是不行的。今天的任务就是:你百度解决汉字的绘制。
小王直到下午才来找我汇报工作。
他不但展示了效果,还跟我汇报了代码。
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
img = Image.new("RGB", (320, 320), "black")
draw = ImageDraw.Draw(img)
# 加载一种字体, 320是字体的大小,和黑框一样大
font = ImageFont.truetype("chinese_fonts/fangzheng_heiti.TTF", 320)
# 将字体作为参数传入
draw.text((0,0), "汉", (255, 255, 255),font)
img.save("汉.png")
跟昨天的相比,区别就是调用 ImageFont.truetype("字体文件路径", 字体大小)
加载了字体文件,然后调用draw.text(……,font)
的时候,把字体font
传入,这样字体大小也可以控制了。
我连连点头,称赞小王很棒。并继续说,汉字的可以了,你再试试数字和符号。
小王又调用draw.text(",")、raw.text("2")
draw了个“,”和“2”。
“什么感觉?看完这些图片,你什么感觉?”,我问小王,声音有些严厉。
“没什么感觉啊,这……这不挺好的!”,小王回答道。
我提高了音量,敲着屏幕:“不居中啊,大哥,逗号那么明显,你看不出来吗?”。
刚刚还沉浸在骄傲中的小王有些疑惑,但是他依然很镇定:这个好弄,draw.text((x,y),……)
我改下x和y坐标就行了。
“今天能改完吗?”,我问他。
“肯定能改完!调个坐标就完事了”,小王很自信的样子。
“好”,我告诉小王:“你要记得一件事,不要针对某一个字符调坐标,多调几个,不管是出1,2,3,4,还是甲乙丙丁,都要居中,记住了吗?”。
下午,我回家时,小王说要加个班。
凌晨2点,我在家上厕所时,远程查看了一下公司的网络流量数据,不断有关于“python”、“字体居中”的搜索。
第三天:字体居中,添加椒盐
第三天一早,我在家吃过饭,又从楼下买了包子和豆浆。
一进办公室,我发现小王趴在办公桌上不动了。
我心里就是一惊,别再是怎么着了吧。昨天的任务对他来说可真是够难的,我连忙晃动他:小王,醒醒,醒醒,小王!
小王慢慢地睁开眼,打了个哈欠,伸了个懒腰:天亮了吗?
小王发现我在旁边,才反应过来:哦,这是在公司啊,我的“居中”功能还没有实现呢!
老大,为什么我怎么调都有问题,
x=20,y=30
,对这个字符可以,换别字符的就不行了呢?如何才能写出通用的代码啊?那得加多少if
和else if
判断啊?
我说,你先把早饭吃了,完了我告诉你个知识点,你马上就能完成任务了。
其实,字体制作时,有很多规则。图中红框表示的区域,代表这是字体的范围,就算内部是空白也是人家的领地。里面,还有一个字符区域,是具体显示的内容,比如逗号,
就靠下,为了便于标记,字符在字体内有一个偏移量offset
属性,表示它相对于字体区域的偏移情况。
所以,你要让一个字在背景里居中,他的坐标绝不是你肉眼看到的,而且是千变万化的,需要你结合字体的宽高以及偏移量来计算。
小王很兴奋,有规则就好办了。
很快,他就写好了代码。
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
width,height = 32,32 # 因为宽高多处使用,定义成变量
font_size = 32
char = "好" # 要绘制的字符
img = Image.new("RGB", (width, height), "black")
draw = ImageDraw.Draw(img)
# 加载一种字体, 32是字体的大小,和黑框一样大
font = ImageFont.truetype("chinese_fonts/fangzheng_fangsong.ttf", font_size)
# 获取字体的宽高
font_width, font_height = draw.textsize(char, font)
offset_x, offset_y = font.getoffset(char)
# 计算字体绘制的x,y坐标,主要是让文字画在图标中心
x = (width - font_width - offset_x) // 2
y = (height - font_height - offset_y) // 2
# 将字体作为参数传入
draw.text((x,y), char, (255,255, 255),font)
img.save("好.png")
小王试了试,不管是数字、字母、符号还是汉字,确实都可以居中了。
其实,关键点就是通过draw.textsize(char, font)
获取了字体的宽高,通过font.getoffset(char)
获取了偏移量的信息。如果要将字体在一个背景中居中,其实就是背景的长度减去字体长度再减去偏移量长度,这是字符和背景的缝隙,缝隙除以2,那就是把缝隙分到两边了,它就居中了。
今天,小王虽然没有睡觉,但是他却很开心,我告诉他帮公司解决了一个大难题,让他早早地回去休息了。
今天晚上,老板应该出差回来了。
我拿出3天前就写好的代码,跑了起来。
from __future__ import print_function
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
import os
import shutil
import time
import cv2
# 要生成的文本
label_dict = {0: '你', 1: '好', 2: '掘', 3: '金', 4: ':', 5: '1', 6: '+', 7: '2', 8: ',', 9: 'g', 10: 'o', 11: '!'}
# 文本对应的文件夹,给每一个分类建一个文件
for value,char in label_dict.items():
train_images_dir = "dataset"+"/"+str(value)
if os.path.isdir(train_images_dir):
shutil.rmtree(train_images_dir)
os.makedirs(train_images_dir)
def makeImage(label_dict, font_path, width=32, height=32, rotate = 0, salt = 22):
# 从字典中取出键值对
for value,char in label_dict.items():
# 创建一个黑色背景的图片
img = Image.new("RGB", (width, height), "black")
draw = ImageDraw.Draw(img)
# 加载一种字体,字体大小是图片宽度的90%
font = ImageFont.truetype(font_path, int(width*0.9))
# 获取字体的宽高
font_width, font_height = draw.textsize(char, font)
offset_x, offset_y = font.getoffset(char)
# 计算字体绘制的x,y坐标,主要是让文字画在图标中心
x = (width - font_width - offset_x) // 2
y = (height - font_height - offset_y) // 2
# 绘制图片,在那里画,画啥,什么颜色,什么字体
draw.text((x,y), char, (255, 255, 255), font)
# 设置图片倾斜角度
if rotate != 0:
img = img.rotate(rotate)
# 将数据转为np格式
np_img = np.asarray(img.getdata(), dtype='uint8')
# 降维,3通道转为1通道,并组成矩阵
np_img = np_img[:, 0].reshape((height, width))
for i in range(salt): #添加噪声
temp_x = np.random.randint(0,np_img.shape[0])
temp_y = np.random.randint(0,np_img.shape[1])
np_img[temp_x][temp_y] = 255
# 命名文件保存,命名规则:dataset/编号/img-编号_r-选择角度_时间戳.png
time_value = int(round(time.time() * 1000))
img_path = "dataset/{}/{}_{}.png".format(value, time_value, rotate)
cv2.imwrite(img_path, np_img)
# 存放字体的路径
font_dir = "./chinese_fonts"
for font_name in os.listdir(font_dir):
# 把每种字体都取出来,每种字体都生成一批图片
path_font_file = os.path.join(font_dir, font_name)
# 倾斜角度从-5到5度,每个角度都生成一批图片
for k in range(-5, 5, 1):
# 每个字符都生成图片
makeImage(label_dict, path_font_file, rotate = k, salt = 5-k)
このコードは、テキストを生成するだけでなく、画像にランダムノイズを追加するなど、画像を適度に回転させるなどの干渉項目を追加します。これをソルトアンドペッパーと呼びます(ペッパーは黒、ソルトは白、つまり白黒ノイズを意味します) )。なぜなら、私たちが特定した文書が与えられると、状況が不明確になるため、干渉に応じて訓練し、効果を実際の状況に近づける必要があるからです。
実際、文字セットを生成するコードは非常に単純で、私は上司と議論した午後にそれを書きました。
今回苦労しているのは、シャオ・ワンに書かせてもらうかどうか。Xiao Wangに書かせてください、私は彼に言った作品を書き終えることができるので、私は数倍のエネルギーを費やす必要があります。Xiao Wangはすでに会社から10人目のインターンであり、最初の数人は少し学んで去ったことを思い出しました。
結局、私はシャオ・ワンを栽培することにしました。最善を尽くし、運命に耳を傾けてください。
たった3日ですが、シャオ・ワンは大きな進歩を遂げたようです。
4日目:クロスオーバー
キャラクターセットを上司に渡して、XiaoWangによって開発されたと言いました。上司はとても幸せで、シャオ・ワンに200元の昇給を与えると言った。
シャオ・ワンに言うかどうか迷っていました。シャオ・ワンは私を見つけました、彼は少し恥ずかしかったです。
実は私も考えました。
Xiao Wang氏は次のように述べています。上司、私は新しい仕事を見つけました。相手は私の能力を非常に高く評価しました。特に、キャラクターセットを自動的に生成する能力については、彼らもそれを非常に必要としており、給料は2倍になりました... ..
「さて、お大事に!いつ出発するの?」と、心に少しも波がありませんでした。
王暁は緊急に言った:今日の午後...大丈夫ですか?
できる!
Xiao Wangが去った駅を見て、引き出しから計画を取り出しました。これは、XiaoWangがOCR認識プロジェクト全体を完了するように訓練するためのスクリプトでした。
私は何気なくページをめくりましたが、このページの知識のポイントは次のとおりです。画像のトレーニングデータセットが主に黒の背景と白のフォントを使用しているのはなぜですか。
私は微笑んだ。それは、黒の色の値が0で、白の色の値が255だからです。コンピューターは0を無視し、白の255にもっと注意を払います。白地に黒の場合、コンピュータに0に焦点を合わせるように要求すると、苦痛になります。
トレーニング方法、調整方法、展開方法などもあります。
とにかく、彼は行くのに良い場所があり、私はビジネスマンと見なすことができます。
交差した、すべて自分自身を交差させた。
ナゲッツテクノロジーコミュニティのクリエイター署名プログラムの募集に参加しています。リンクをクリックして登録し、送信してください。