對于七段數碼數字模型進行改進:一個關鍵的數字1的問題
簡 介:
對于訓練集合進行擴增,需要根據圖片本身在應用中可能遇到的變化進行。對于圖片中的數碼管數字識別,一個最重要的問題是字符的平移,特別是對于字符1來說,遇到的可能性最大。比如在一些三位半,四位半的數字表中,最前面的數字可能只有1,0兩個數字,所以分割過程中,1的位置有可能位于圖片的最左。針對這種情況,對于訓練數據集合進行平移擴充,通過測試結果可以看出,模型的精度得到了提高。
關鍵詞:
數碼管,LENET,CNN,數據增強
數字1的問題
文章目錄
問題來源
如何解決?
重新訓練
準備數據集合
訓練LeNet
檢驗數字問題
數字平移
數據準備
訓練LCDNet
測試網絡
總 結
資源下載
模型應用
數字1的問題
文章目錄
問題來源
如何解決?
重新訓練
準備數據集合
訓練LeNet
檢驗數字問題
數字平移
數據準備
訓練LCDNet
測試網絡
總 結
資源下載
模型應用
1.1 問題來源
在 一個中等規模的七段數碼數據庫以及利用它訓練的識別網絡 中,利用了近200張網絡數碼圖片訓練出LeNet網絡,可以達到了很好的數字(LCD,LED七段數字)識別的效果,網絡的適應性比較強。但是在 測試LCDNet對于萬用表讀數識別效果 測試中,對于如下的圖片識別的結果出現了問題:
下面的圖片被識別成“07729”
問題出現在對于字符分割的問題上,明顯,對于最左側的“1”,實際上它的左側部分被切割出去了。因此,將上述圖片按照5等分,所獲得到的圖片如下。如果注視到這個分割結果,對于第一個字符應該說,還是分割的不錯的。主要原因是“1”所在的位置偏向中心。
為了驗證這個問題,對原來圖片左側進行填補背景顏色,對應的圖片如下。
然后再進行五等分,對應的圖片為:
在這種情況下,所獲得的識別結果就正確了。
這說明在原來訓練模型中,對于“1”這個字符,更多的樣本對應“1”它是在圖片的左側,而不是在中間或者右邊。
1.2 如何解決?
上面的這種情況對于數字“1”比較特殊,一種簡單的解決方案,就是直接對于樣本中所有的“1”的樣本,都進行左右平移的擴充,使得模型對于“1”的左右位置不敏感。
plt.figure(figsize=(10, 5)) n = inp[0][0] x = list(range(0, 24, 4)) print(type(n), shape(n), x) for id,xx in enumerate(x): mm = roll(n, xx) plt.subplot(1, len(x), id+1) plt.imshow(mm)
1
2
3
4
5
6
7
8
9
2.1 準備數據集合
2.1.1 數據集合進行合并
現在已經有了四個7段數字圖片集合,將它們合并在一起。
輸入數字目錄:7seg, testlcd, testled, testseg7
輸出數字目錄:seg7all
最終獲得數字圖片:303個
from headm import * # = import shutil inputdir = ['7Seg', 'testlcd', 'testled', 'testseg7'] outdir = '/home/aistudio/work/7seg/seg7all' count = 0 for d in inputdir: dstr = '/home/aistudio/work/7seg/' + d fdir = os.listdir(dstr) for f in fdir: ext = f.split('.')[-1].upper() if ext.find('JPG') < 0 and ext.find('BMP') < 0: continue numstr = f.split('.')[0].split('-')[-1] outfn = '%03d-%s.%s'%(count, numstr, ext) outfn = os.path.join(outdir, outfn) count += 1 shutil.copyfile(os.path.join(dstr, f), outfn) printt(count)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2.1.2 分解圖片中的數字
對前面準備好的數字圖片分割相應的數字。
from headm import * # = import cv2 from tqdm import tqdm picdir = '/home/aistudio/work/7seg/seg7all' filedim = [s for s in os.listdir(picdir) if s.upper().find('BMP') > 0 or s.upper().find('JPG') > 0] filedim = sorted(filedim) outdir = '/home/aistudio/work/7seg/pic48' totalpic = 0 OUT_SIZE = 48 for f in tqdm(filedim): fn = os.path.join(picdir, f) img = cv2.imread(fn) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) imgwidth = gray.shape[1] imgheight = gray.shape[0] numstr = f.split('.')[0].split('-')[1] numnum = len(numstr) for i in range(numnum): left = imgwidth * i // numnum right = imgwidth * (i + 1) // numnum data = gray[0:imgheight, left:right] dataout = cv2.resize(data, (OUT_SIZE, OUT_SIZE)) outfn = os.path.join(outdir, '%04d_%s.BMP'%(totalpic, numstr[i])) totalpic += 1 cv2.imwrite(outfn, dataout) newheight = int(imgheight * 0.85) newwidth = int((right-left)*0.85) deltaheight = (imgheight- newheight)//2 deltawidth = (right-left-newwidth)//2 data = gray[deltaheight:imgheight-deltaheight, left:right] dataout = cv2.resize(data, (OUT_SIZE, OUT_SIZE)) outfn = os.path.join(outdir, '%04d_%s.BMP'%(totalpic, numstr[i])) totalpic += 1 cv2.imwrite(outfn, dataout) data = gray[0:imgheight, left+deltawidth:right-deltawidth] dataout = cv2.resize(data, (OUT_SIZE, OUT_SIZE)) outfn = os.path.join(outdir, '%04d_%s.BMP'%(totalpic, numstr[i])) totalpic += 1 cv2.imwrite(outfn, dataout) data = gray[deltaheight:imgheight-deltaheight, left+deltawidth:right-deltawidth] dataout = cv2.resize(data, (OUT_SIZE, OUT_SIZE)) outfn = os.path.join(outdir, '%04d_%s.BMP'%(totalpic, numstr[i])) totalpic += 1 cv2.imwrite(outfn, dataout) printt(totalpic:)
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
分割完畢之后,每個數字對應四個數字,分別是原來數字,上下左右膨脹1.17倍的圖片。圖片總數為: 5340。
2.1.3 數字清洗與1平移
下面對于分割出的數字進行清洗,其中包含有“N”背景顏色的數字。另外,對于所有為“1”的數字往右平移倍增。
if num == 1: img = cv2.imread(infn) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray1 = roll(gray, -12) gray2 = roll(gray, -24) gray3 = roll(gray, -36)
1
2
3
4
5
6
可以看到實際上,只需要平移12,24兩個即可。
處理完之后,總共的數字個數: 6548,各個數字的分布如下,可以看到其中數字1已經倍增了3倍。
from headm import * # = import shutil import cv2 digitdir = '/home/aistudio/work/7seg/pic48' outdir = '/home/aistudio/work/7seg/pic48_1' filedim = sorted(os.listdir(digitdir)) printt(len(filedim)) label = [] count = 0 for f in filedim: infn = os.path.join(digitdir, f) nstr = f.split('.')[0].split('_')[-1] if not nstr.isdigit(): continue extstr = f.split('.')[-1] num = int(nstr) outfn = os.path.join(outdir, '%05d_%d.%s'%(count, num, extstr)) count += 1 label.append(num) shutil.copyfile(infn, outfn) if num == 1: img = cv2.imread(infn) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray1 = roll(gray, -12) gray2 = roll(gray, -24) outfn = os.path.join(outdir, '%05d_%d.%s'%(count, num, extstr)) count += 1 cv2.imwrite(outfn, gray1) label.append(num) outfn = os.path.join(outdir, '%05d_%d.%s'%(count, num, extstr)) count += 1 cv2.imwrite(outfn, gray2) label.append(num) printt(count) plt.figure(figsize=(10,6)) plt.hist(label, 10) plt.xlabel("N") plt.ylabel("Frequency") plt.grid(True) plt.tight_layout() plt.show()
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
2.1.4 圖片數據歸一化
將生成的圖片目錄中的數據轉換成歸一化的圖片數據。
from headm import * # = import paddle import paddle.fluid as fluid import paddle.nn.functional as F from paddle import to_tensor as TT import cv2 outdir = '/home/aistudio/work/7seg/pic48_1' filedim = sorted([s for s in os.listdir(outdir) if s.find('BMP') > 0 or s.find('JPG') > 0]) printt(len(filedim)) picarray = [] labeldim = [] for f in filedim: fn = os.path.join(outdir, f) img = cv2.imread(fn) gray = img[:,:,0] gray = gray - mean(gray) stdd = std(gray) gray1 = gray / stdd gray2 = gray * (-1.0) ff = f.split('.')[0].split('_')[-1] if ff.isdigit(): ff = int(ff) picarray.append(gray1) picarray.append(gray2) labeldim.append(ff) labeldim.append(ff) printt(shape(picarray)) printt(labeldim) outfile = '/home/aistudio/work/7seg/seg7_48_4_1.npz' savez(outfile, pic=picarray, label=labeldim)
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
處理完之后,數據個數:
2.2 訓練LeNet
利用與 一個中等規模的七段數碼數據庫以及利用它訓練的識別網絡 相同的網絡,對于剛剛生成的數據庫進行訓練。
訓練環境: AI Studio,智尊版本。
訓練存儲模型:seg7model4_1.pdparams
2.2.1 訓練代碼
from headm import * # = import paddle import paddle.fluid as fluid from paddle import to_tensor as TT from paddle.nn.functional import square_error_cost as SQRC datafile = '/home/aistudio/work/7seg/seg7_48_4_1.npz' data = load(datafile) lcd = data['pic'] llabel = data['label'] printt(lcd.shape, llabel.shape) dl = [(d,l) for d,l in zip(lcd, llabel)] random.shuffle(dl) printt(shape(dl)) train_ratio = 0.8 train_num = int(len(llabel) * train_ratio) train_lcd = [a[0] for a in dl[:train_num]] train_label = [a[1] for a in dl[:train_num]] test_lcd = array([a[0] for a in dl[train_num:]]) test_label = array([a[1] for a in dl[train_num:]]) class Dataset(paddle.io.Dataset): def __init__(self, num_samples): super(Dataset, self).__init__() self.num_samples = num_samples def __getitem__(self, index): data = train_lcd[index][newaxis,:,:] label = train_label[index] return paddle.to_tensor(data,dtype='float32'), paddle.to_tensor(label,dtype='int64') def __len__(self): return self.num_samples _dataset = Dataset(len(train_label)) train_loader = paddle.io.DataLoader(_dataset, batch_size=1000, shuffle=True) test_d = paddle.to_tensor([a[newaxis,:,:] for a in test_lcd], dtype='float32') test_l = paddle.to_tensor(test_label[:, newaxis], dtype='int64') printt(shape(test_d):, shape(test_l):) imgwidth = 48 imgheight = 48 inputchannel = 1 kernelsize = 5 targetsize = 10 ftwidth = ((imgwidth-kernelsize+1)//2-kernelsize+1)//2 ftheight = ((imgheight-kernelsize+1)//2-kernelsize+1)//2 class lenet(paddle.nn.Layer): def __init__(self, ): super(lenet, self).__init__() self.conv1 = paddle.nn.Conv2D(in_channels=inputchannel, out_channels=6, kernel_size=kernelsize, stride=1, padding=0) self.conv2 = paddle.nn.Conv2D(in_channels=6, out_channels=16, kernel_size=kernelsize, stride=1, padding=0) self.mp1 = paddle.nn.MaxPool2D(kernel_size=2, stride=2) self.mp2 = paddle.nn.MaxPool2D(kernel_size=2, stride=2) self.L1 = paddle.nn.Linear(in_features=ftwidth*ftheight*16, out_features=120) self.L2 = paddle.nn.Linear(in_features=120, out_features=86) self.L3 = paddle.nn.Linear(in_features=86, out_features=targetsize) def forward(self, x): x = self.conv1(x) x = paddle.nn.functional.relu(x) x = self.mp1(x) x = self.conv2(x) x = paddle.nn.functional.relu(x) x = self.mp2(x) x = paddle.flatten(x, start_axis=1, stop_axis=-1) x = self.L1(x) x = paddle.nn.functional.relu(x) x = self.L2(x) x = paddle.nn.functional.relu(x) x = self.L3(x) return x model = lenet() optimizer = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters()) def train(model): model.train() epochs = 200 for epoch in range(epochs): for batch, data in enumerate(train_loader()): out = model(data[0]) loss = paddle.nn.functional.cross_entropy(out, data[1]) acc = paddle.metric.accuracy(out, data[1]).numpy() preout = model(test_d) test_acc = paddle.metric.accuracy(preout, test_l).numpy() loss.backward() optimizer.step() optimizer.clear_grad() printt('Epoch:{}, Accuracys:{}, Test:{}'.format(epoch, acc, test_acc)) train(model) paddle.save(model.state_dict(), './work/seg7model4_1.pdparams') filename = '/home/aistudio/stdout.txt' accdim = [] testdim = [] with open(filename, 'r') as f: for l in f.readlines(): ll = l.split(':[') if len(ll) < 3: continue lacc = ll[-2].split(']') if len(lacc) < 2: continue accdim.append(float(lacc[0])) tacc = ll[-1].split(']') if len(tacc) < 2: continue testdim.append(float(tacc[0])) plt.figure(figsize=(12, 8)) plt.plot(accdim, label='Train ACC') plt.plot(testdim, label='Test ACC') plt.xlabel("Step") plt.ylabel("Acc") plt.grid(True) plt.legend(loc='upper right') plt.tight_layout() plt.show()
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
2.3 檢驗數字問題
利用訓練后所得到的模型,重新建議前面數字1所碰到的問題。
最終的識別結果,無論是原來的圖片,還是之后左邊補充背景顏色的圖片,識別結構都正常了。
這說明通過平移1圖片對于模型性能的提高是起到很重要的作用的。
根
據前面的結果,下面對所有的數字都進行平移擴增,只是對“1”是往左平移12,24,對于其它的數字往左右各平移6,形成倍增后的數字。
3.1 數據準備
3.1.1 平移數字
from headm import * # = import shutil import cv2 digitdir = '/home/aistudio/work/7seg/pic48' outdir = '/home/aistudio/work/7seg/pic48_1' filedim = sorted(os.listdir(digitdir)) printt(len(filedim)) label = [] count = 0 for f in filedim: infn = os.path.join(digitdir, f) nstr = f.split('.')[0].split('_')[-1] if not nstr.isdigit(): continue extstr = f.split('.')[-1] num = int(nstr) outfn = os.path.join(outdir, '%05d_%d.%s'%(count, num, extstr)) count += 1 label.append(num) shutil.copyfile(infn, outfn) if num == 1: img = cv2.imread(infn) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray1 = roll(gray, -12) gray2 = roll(gray, -24) outfn = os.path.join(outdir, '%05d_%d.%s'%(count, num, extstr)) count += 1 cv2.imwrite(outfn, gray1) label.append(num) outfn = os.path.join(outdir, '%05d_%d.%s'%(count, num, extstr)) count += 1 cv2.imwrite(outfn, gray2) label.append(num) else: img = cv2.imread(infn) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray1 = roll(gray, 6) gray2 = roll(gray, -6) outfn = os.path.join(outdir, '%05d_%d.%s'%(count, num, extstr)) count += 1 cv2.imwrite(outfn, gray1) label.append(num) outfn = os.path.join(outdir, '%05d_%d.%s'%(count, num, extstr)) count += 1 cv2.imwrite(outfn, gray2) label.append(num) printt(count) plt.figure(figsize=(10,6)) plt.hist(label, 10) plt.xlabel("N") plt.ylabel("Frequency") plt.grid(True) plt.tight_layout() plt.show()
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
評議結果:個數:15948.
3.1.2 數據歸一化
數據個數:43384
數據尺寸:48×48
數據文件:seg7_48_4_1_all.npz
3.2 訓練LCDNet
訓練參數與前面相同,訓練后模型存入:
/work/seg7model4_1_all.pdparams
BatchSize:1000
Lr:0.001
Epoch:200
訓練數據量:43384
訓練/測試占比:0.8:0.2
最終的訓練精度:
Train Accuarcy: 1.0
Test Accuarcy: 0.991
3.3 測試網絡
利用該模型,對于303個七段數碼管數字識別,進行測試。
總共有兩個圖片識別存在錯誤:
對
于訓練集合進行擴增,需要根據圖片本身在應用中可能遇到的變化進行。對于圖片中的數碼管數字識別,一個最重要的問題是字符的平移,特別是對于字符1來說,遇到的可能性最大。比如在一些三位半,四位半的數字表中,最前面的數字可能只有1,0兩個數字,所以分割過程中,1的位置有可能位于圖片的最左。
針對這種情況,對于訓練數據集合進行平移擴充,通過測試結果可以看出,模型的精度得到了提高。
4.1 資源下載
AI Studio數據集合:擴增后的數據集合
CSDN上下載鏈接:七段數碼管測試數據集合,LENET訓練好的模型
4.2 模型應用
from headm import * # = import paddle import paddle.fluid as fluid import cv2 imgwidth = 48 imgheight = 48 inputchannel = 1 kernelsize = 5 targetsize = 10 ftwidth = ((imgwidth-kernelsize+1)//2-kernelsize+1)//2 ftheight = ((imgheight-kernelsize+1)//2-kernelsize+1)//2 class lenet(paddle.nn.Layer): def __init__(self, ): super(lenet, self).__init__() self.conv1 = paddle.nn.Conv2D(in_channels=inputchannel, out_channels=6, kernel_size=kernelsize, stride=1, padding=0) self.conv2 = paddle.nn.Conv2D(in_channels=6, out_channels=16, kernel_size=kernelsize, stride=1, padding=0) self.mp1 = paddle.nn.MaxPool2D(kernel_size=2, stride=2) self.mp2 = paddle.nn.MaxPool2D(kernel_size=2, stride=2) self.L1 = paddle.nn.Linear(in_features=ftwidth*ftheight*16, out_features=120) self.L2 = paddle.nn.Linear(in_features=120, out_features=86) self.L3 = paddle.nn.Linear(in_features=86, out_features=targetsize) def forward(self, x): x = self.conv1(x) x = paddle.nn.functional.relu(x) x = self.mp1(x) x = self.conv2(x) x = paddle.nn.functional.relu(x) x = self.mp2(x) x = paddle.flatten(x, start_axis=1, stop_axis=-1) x = self.L1(x) x = paddle.nn.functional.relu(x) x = self.L2(x) x = paddle.nn.functional.relu(x) x = self.L3(x) return x model = lenet() model.set_state_dict(paddle.load('./work/seg7model4_1_all.pdparams')) OUT_SIZE = 48 def pic2netinput(imgfile): img = cv2.imread(imgfile) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) imgwidth = gray.shape[1] imgheight = gray.shape[0] f = os.path.basename(imgfile) numstr = f.split('.')[0].split('-')[1] numnum = len(numstr) imgarray = [] labeldim = [] for i in range(numnum): left = imgwidth * i // numnum right = imgwidth * (i + 1) // numnum data = gray[0:imgheight, left:right] dataout = cv2.resize(data, (OUT_SIZE, OUT_SIZE)) dataout = dataout - mean(dataout) stdd = std(dataout) dataout = dataout / stdd if numstr[i].isdigit(): imgarray.append(dataout[newaxis,:,:]) labeldim.append(int(numstr[i])) test_i = paddle.to_tensor(imgarray, dtype='float32') test_label = array(labeldim) test_l = paddle.to_tensor(test_label[:, newaxis], dtype='int64') return test_i, test_l picdir = '/home/aistudio/work/7seg/seg7all' filedim = [s for s in os.listdir(picdir) if s.upper().find('BMP') > 0 or s.upper().find('JPG') > 0] filedim = sorted(filedim) def checkimglabel(imgfile): inp, label = pic2netinput(imgfile) preout = model(inp) preid = paddle.argmax(preout, axis=1).numpy().flatten() label = label.numpy().flatten() error = where(label != preid)[0] printt(preid:, label:) ''' inp = inp.numpy() plt.figure(figsize=(10, 5)) n = inp[0][0] x = list(range(0, 24, 4)) printt(type(n), shape(n), x) for id,xx in enumerate(x): mm = roll(n, xx) plt.subplot(1, len(x), id+1) plt.imshow(mm) ''' return error, preid ''' imgfile = os.path.join(picdir, filedim[-1]) error,id = checkimglabel(imgfile) printt(error:, id:) ''' for f in filedim: imgfile = os.path.join(picdir, f) error,id = checkimglabel(imgfile) if len(error) > 0: printt(error, f, id) img = cv2.imread(imgfile)[:,:,::-1] plt.clf() plt.figure(figsize=(8,8)) plt.axis("off") plt.imshow(img) plt.show()
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
■ 相關文獻鏈接:
一個中等規模的七段數碼數據庫以及利用它訓練的識別網絡
測試LCDNet對于萬用表讀數識別效果
AI Studio數據集合:擴增后的數據集合
七段數碼管測試數據集合,LENET訓練好的模型-深度學習文檔類資源-CSDN文庫
● 相關圖表鏈接:
圖1.1 圖片內容被識別成07729
圖1.1.2 圖片分為5等分對應的圖片
圖1.1.3 對原來圖片左側進行填補背景之后的圖片
圖1.1.4 補充分割之后的圖片
圖1.2.1 將1圖片左右平移
圖2.1.1 十個數字的不同頻次分布
圖2.1.2 往右分別平移 12,24,36
圖2.1.3 處理完之后的數字分布
圖2.2.1 訓練曲線:訓練精度和測試精度
圖3.2.1 訓練過程的精度變化曲線
圖A3.3.1 測試訓練樣本
圖3.3.1 識別為:1824
圖3.3.2 識別為:1466
機器學習 電商家電數碼
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。