-
Notifications
You must be signed in to change notification settings - Fork 93
/
img_tools.py
316 lines (268 loc) · 9.55 KB
/
img_tools.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
"""
一些图像处理工具
"""
import os
from PIL import Image
from cfg import img_path, bin_clear_folder, origin_pic_folder, cut_pic_folder, data_root
from os.path import join
def get_bin_table(threshold=140):
"""
获取灰度转二值的映射table
:param threshold:
:return:
"""
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
return table
def sum_9_region(img, x, y):
"""
9邻域框,以当前点为中心的田字框,黑点个数,作为移除一些孤立的点的判断依据
:param img: Image
:param x:
:param y:
:return:
"""
cur_pixel = img.getpixel((x, y)) # 当前像素点的值
width = img.width
height = img.height
if cur_pixel == 1: # 如果当前点为白色区域,则不统计邻域值
return 0
if y == 0: # 第一行
if x == 0: # 左上顶点,4邻域
# 中心点旁边3个点
sum = cur_pixel \
+ img.getpixel((x, y + 1)) \
+ img.getpixel((x + 1, y)) \
+ img.getpixel((x + 1, y + 1))
return 4 - sum
elif x == width - 1: # 右上顶点
sum = cur_pixel \
+ img.getpixel((x, y + 1)) \
+ img.getpixel((x - 1, y)) \
+ img.getpixel((x - 1, y + 1))
return 4 - sum
else: # 最上非顶点,6邻域
sum = img.getpixel((x - 1, y)) \
+ img.getpixel((x - 1, y + 1)) \
+ cur_pixel \
+ img.getpixel((x, y + 1)) \
+ img.getpixel((x + 1, y)) \
+ img.getpixel((x + 1, y + 1))
return 6 - sum
elif y == height - 1: # 最下面一行
if x == 0: # 左下顶点
# 中心点旁边3个点
sum = cur_pixel \
+ img.getpixel((x + 1, y)) \
+ img.getpixel((x + 1, y - 1)) \
+ img.getpixel((x, y - 1))
return 4 - sum
elif x == width - 1: # 右下顶点
sum = cur_pixel \
+ img.getpixel((x, y - 1)) \
+ img.getpixel((x - 1, y)) \
+ img.getpixel((x - 1, y - 1))
return 4 - sum
else: # 最下非顶点,6邻域
sum = cur_pixel \
+ img.getpixel((x - 1, y)) \
+ img.getpixel((x + 1, y)) \
+ img.getpixel((x, y - 1)) \
+ img.getpixel((x - 1, y - 1)) \
+ img.getpixel((x + 1, y - 1))
return 6 - sum
else: # y不在边界
if x == 0: # 左边非顶点
sum = img.getpixel((x, y - 1)) \
+ cur_pixel \
+ img.getpixel((x, y + 1)) \
+ img.getpixel((x + 1, y - 1)) \
+ img.getpixel((x + 1, y)) \
+ img.getpixel((x + 1, y + 1))
return 6 - sum
elif x == width - 1: # 右边非顶点
# print('%s,%s' % (x, y))
sum = img.getpixel((x, y - 1)) \
+ cur_pixel \
+ img.getpixel((x, y + 1)) \
+ img.getpixel((x - 1, y - 1)) \
+ img.getpixel((x - 1, y)) \
+ img.getpixel((x - 1, y + 1))
return 6 - sum
else: # 具备9领域条件的
sum = img.getpixel((x - 1, y - 1)) \
+ img.getpixel((x - 1, y)) \
+ img.getpixel((x - 1, y + 1)) \
+ img.getpixel((x, y - 1)) \
+ cur_pixel \
+ img.getpixel((x, y + 1)) \
+ img.getpixel((x + 1, y - 1)) \
+ img.getpixel((x + 1, y)) \
+ img.getpixel((x + 1, y + 1))
return 9 - sum
def remove_noise_pixel(img, noise_point_list):
"""
根据噪点的位置信息,消除二值图片的黑点噪声
:type img:Image
:param img:
:param noise_point_list:
:return:
"""
for item in noise_point_list:
img.putpixel((item[0], item[1]), 1)
def get_clear_bin_image(image):
"""
获取干净的二值化的图片。
图像的预处理:
1. 先转化为灰度
2. 再二值化
3. 然后清除噪点
参考:http://python.jobbole.com/84625/
:type img:Image
:return:
"""
imgry = image.convert('L') # 转化为灰度图
table = get_bin_table()
out = imgry.point(table, '1') # 变成二值图片:0表示黑色,1表示白色
noise_point_list = [] # 通过算法找出噪声点,第一步比较严格,可能会有些误删除的噪点
for x in range(out.width):
for y in range(out.height):
res_9 = sum_9_region(out, x, y)
if (0 < res_9 < 3) and out.getpixel((x, y)) == 0: # 找到孤立点
pos = (x, y) #
noise_point_list.append(pos)
remove_noise_pixel(out, noise_point_list)
return out
def get_crop_imgs(img):
"""
按照图片的特点,进行切割,这个要根据具体的验证码来进行工作. # 见本例验证图的结构原理图
分割图片是传统机器学习来识别验证码的重难点,如果这一步顺利的话,则多位验证码的问题可以转化为1位验证字符的识别问题
:param img:
:return:
"""
child_img_list = []
for i in range(4):
x = 2 + i * (6 + 4) # 见原理图
y = 0
child_img = img.crop((x, y, x + 6, y + 10))
child_img_list.append(child_img)
return child_img_list
def print_line_x(img, x):
"""
打印一个Image图像的第x行,方便调试
:param img:
:type img:Image
:param x:
:return:
"""
print("line:%s" % x)
for w in range(img.width):
print(img.getpixel((w, x)), end='')
print('')
def print_bin(img):
"""
输出二值后的图片到控制台,方便调试的函数
:param img:
:type img: Image
:return:
"""
print('current binary output,width:%s-height:%s\n')
for h in range(img.height):
for w in range(img.width):
print(img.getpixel((w, h)), end='')
print('')
def save_crop_imgs(bin_clear_image_path, child_img_list):
"""
输入:整个干净的二化图片
输出:每张切成4版后的图片集
保存切割的图片
例如: A.png ---> A-1.png,A-2.png,... A-4.png 并保存,这个保存后需要去做label标记的
:param bin_clear_image_path: xxxx/xxxxx/xxxxx.png 主要是用来提取切割的子图保存的文件名称
:param child_img_list:
:return:
"""
full_file_name = os.path.basename(bin_clear_image_path) # 文件名称
full_file_name_split = full_file_name.split('.')
file_name = full_file_name_split[0]
# file_ext = full_file_name_split[1]
i = 0
for child_img in child_img_list:
cut_img_file_name = file_name + '-' + ("%s.png" % i)
child_img.save(join(cut_pic_folder, cut_img_file_name))
i += 1
# 训练素材准备:文件目录下面的图片的批量操作
def batch_get_all_bin_clear():
"""
训练素材准备。
批量操作:获取所有去噪声的二值图片
:return:
"""
file_list = os.listdir(origin_pic_folder)
for file_name in file_list:
file_full_path = os.path.join(origin_pic_folder, file_name)
image = Image.open(file_full_path)
get_clear_bin_image(image)
def batch_cut_images():
"""
训练素材准备。
批量操作:分割切除所有 "二值 -> 除噪声" 之后的图片,变成所有的单字符的图片。然后保存到相应的目录,方便打标签
"""
file_list = os.listdir(bin_clear_folder)
for file_name in file_list:
bin_clear_img_path = os.path.join(bin_clear_folder, file_name)
img = Image.open(bin_clear_img_path)
child_img_list = get_crop_imgs(img)
save_crop_imgs(bin_clear_img_path, child_img_list) # 将切割的图进行保存,后面打标签时要用
# 中间的demo效果演示
def demo_cut_pic():
"""
做实验研究时的演示代码
:return:
"""
img_path = join(data_root, 'demo-6937/ocr-simple-char-captcha-bin-clear-6937.png')
img = Image.open(img_path)
cut_save = data_root + '/demo-6937'
child_img_list = get_crop_imgs(img)
index = 0
for child_img in child_img_list:
child_img.save(cut_save + '/cut-%d.png' % index)
index += 1
def get_bin_img_name(img_path):
"""
根据原始origin 文件路径,获取二值而且去噪声的文件路径
:param img_path:
:type img_path:str
:return:
"""
path_split = img_path.split('/')
file_name_split = path_split[-1].split('.')
file_name = file_name_split[0] # 文件名
# file_ext = file_name_split[1] # 扩展名
new_file = '/'.join(item for item in path_split[:-2]) + '/bin_clear/' + file_name + '.png'
return new_file
def demo_handle_save_bin_clear_pic(image):
"""
图像处理函数的演示
在训练分析阶段的时候使用:保存二次的二值图,
:type img:Image
:return:
"""
out = get_clear_bin_image(image)
new_file_path = get_bin_img_name(img_path)
print(new_file_path)
out.save(new_file_path)
if __name__ == "__main__":
print(get_bin_table())
# batch_get_all_bin_clear() # 获取所有的二值化的初步去噪的图片
# cut_all_pic() # 切割图片成单个字
# save_train_txt()
# save_test_txt()
# crack_captcha()
# img = Image.open(img_path)
# handle_save_bin_clear_pic(img)
# demo_cut_pic()
pass