回答編集履歴

2

実装例追記

2018/11/14 12:28

投稿

fana
fana

スコア11708

test CHANGED
@@ -13,3 +13,189 @@
13
13
  2. これらの情報をもとに,各行(文字のある箇所)のx座標を推定
14
14
 
15
15
  3. 各行位置を下から上へ見ていき,黒範囲を決定
16
+
17
+
18
+
19
+ ---
20
+
21
+
22
+
23
+ 上記のような話を実装.
24
+
25
+ * 当方,pythonがダメなのでC++です.
26
+
27
+ * 各処理のクオリティが色々と雑です.
28
+
29
+ * 質問の画像とは白黒が逆になってます(背景が白で,文字が黒)
30
+
31
+ * 画像左右の文字が無い領域が,余白なのか空行なのか判断できません.
32
+
33
+ ```ここに言語を入力
34
+
35
+ int main(void)
36
+
37
+ {
38
+
39
+ //(0)画像読込
40
+
41
+ cv::Mat Src = cv::imread( "TestLetter.png", CV_LOAD_IMAGE_GRAYSCALE );
42
+
43
+ if( Src.empty() )return 0;
44
+
45
+ cv::imshow( "Src", Src );
46
+
47
+
48
+
49
+ //(1)各x座標における,黒画素の下端y座標を求めておく
50
+
51
+ std::vector<int> YMax( Src.cols, 0 );
52
+
53
+ for( int y=0; y<Src.rows; ++y )
54
+
55
+ {
56
+
57
+ const unsigned char *pI = Src.ptr<unsigned char>(y);
58
+
59
+ for( int x=0; x<Src.cols; ++x, ++pI )
60
+
61
+ {
62
+
63
+ if( *pI < 250 ){ YMax[x] = y; }
64
+
65
+ }
66
+
67
+ }
68
+
69
+ //(2)文字が無いx区間を収集
70
+
71
+ std::vector< std::pair<int,int> > NonLetterReg;
72
+
73
+ int RegLeft = -1;
74
+
75
+ for( int x=0; x<YMax.size(); ++x )
76
+
77
+ {
78
+
79
+ if( RegLeft < 0 )
80
+
81
+ {
82
+
83
+ if( YMax[x]<=0 )
84
+
85
+ { RegLeft=x; }
86
+
87
+ }
88
+
89
+ else
90
+
91
+ {
92
+
93
+ if( YMax[x]>0 )
94
+
95
+ {
96
+
97
+ NonLetterReg.emplace_back( RegLeft, x-1 );
98
+
99
+ RegLeft = -1;
100
+
101
+ }
102
+
103
+ }
104
+
105
+ }
106
+
107
+ if( RegLeft >= 0 )
108
+
109
+ { NonLetterReg.emplace_back( RegLeft, YMax.size()-1 ); }
110
+
111
+
112
+
113
+ //(3)行と行間の幅を推定
114
+
115
+ int LetterWidth = 0; //行の幅
116
+
117
+ {//行の幅をNonLetterRegの隣接要素間の間隔の平均として推定
118
+
119
+ int SumDx = 0;
120
+
121
+ for( int i=0; i+1<NonLetterReg.size(); ++i )
122
+
123
+ { SumDx += NonLetterReg[i+1].first - NonLetterReg[i].second; }
124
+
125
+ LetterWidth = SumDx / (NonLetterReg.size()-1);
126
+
127
+ }
128
+
129
+ int BLWidth = Src.cols; //行間の幅
130
+
131
+ {//行間の幅の推定… 面倒なのでここでは最小値にしちゃう^^
132
+
133
+ for( auto &Reg : NonLetterReg )
134
+
135
+ { BLWidth = std::min( BLWidth, Reg.second-Reg.first+1 ); }
136
+
137
+ }
138
+
139
+
140
+
141
+ //(4)文字の下端位置を各行に関して調べる
142
+
143
+ //※面倒なので結果はデータ化せずに直接画像に描画している
144
+
145
+ cv::Mat Result; //結果描画用の絵
146
+
147
+ cv::cvtColor( Src, Result, CV_GRAY2BGR );
148
+
149
+ {
150
+
151
+ int LetterLeft = NonLetterReg.front().second + 1;
152
+
153
+ while( LetterLeft+LetterWidth-1 < YMax.size() )
154
+
155
+ {
156
+
157
+ //着目行範囲x座標(の推定):LetterLeft~LetterRight
158
+
159
+ int LetterRight = LetterLeft + LetterWidth-1;
160
+
161
+ int Bottom = *std::max_element( YMax.begin()+LetterLeft, YMax.begin()+LetterRight );
162
+
163
+
164
+
165
+ //x範囲全域でYMax[x]が0だったら空行.
166
+
167
+ //そうでない場合…LetterLeftの位置決めがあまりにも雑なのでYMax[LetterLeft]>0になる位置に微調整する
168
+
169
+ if( Bottom>0 && YMax[LetterLeft]<=0 )
170
+
171
+ { ++LetterLeft; continue; } //※座標調整の実装が雑すぎるが
172
+
173
+
174
+
175
+ //結果描画
176
+
177
+ cv::rectangle( Result, cv::Point(LetterLeft,Bottom), cv::Point(LetterRight,Result.rows), cv::Scalar( 0,0,255 ), -1 );
178
+
179
+
180
+
181
+ //次の行(の推定位置)へ
182
+
183
+ LetterLeft = LetterRight + 1 + BLWidth;
184
+
185
+ }
186
+
187
+ }
188
+
189
+
190
+
191
+ cv::imshow( "Result", Result );
192
+
193
+ cv::waitKey();
194
+
195
+ return 0;
196
+
197
+ }
198
+
199
+ ```
200
+
201
+ ![イメージ説明](1b132163d7dd6be33dbc4fb417cf144a.png)

1

追記

2018/11/14 12:28

投稿

fana
fana

スコア11708

test CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  →隣接する領域間の距離の最頻値あたりから,行(文字のある箇所)の幅を推定
8
8
 
9
- →領域のx幅の最貧値あたりから,行間の幅を推定
9
+ →領域のx幅の最貧値あたりから,行間の幅を推定(行間の幅は行の幅よりも小さいとか何とか条件を付けたりして)
10
10
 
11
11
 
12
12