-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathanalyse.cpp
529 lines (470 loc) · 15 KB
/
analyse.cpp
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
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
#pragma region 常量定义
// 单元格状态
const unsigned char COL_DEL = 0; // 单元格已删除
const unsigned char ROW_DEL = 1; // 该行已删除
const unsigned char BG_ITEM = 2; // 纯色单元格
const unsigned char ACT_ITEM = 3; // 有效单元格
#pragma endregion
#pragma region 引入js定义的方法
extern "C"
{
// 输出日志
void jsLog(int num);
}
#pragma endregion
#pragma region 内部方法
#pragma region 快排
bool checkValue(unsigned char *ptr1, unsigned char *ptr2)
{
if (ptr1[0] > ptr2[0] || (ptr1[0] == ptr2[0] && ptr1[1] > ptr2[1]) || (ptr1[0] == ptr2[0] && ptr1[1] == ptr2[1] && ptr1[2] > ptr2[2]))
{
return true;
}
else
{
return true;
}
}
void swapValue(unsigned char *ptr1, unsigned char *ptr2)
{
if (ptr1 != ptr2)
{
unsigned char r = ptr1[0];
unsigned char g = ptr1[1];
unsigned char b = ptr1[2];
ptr1[0] = ptr2[0];
ptr1[1] = ptr2[1];
ptr1[2] = ptr2[2];
ptr2[0] = r;
ptr2[1] = g;
ptr2[2] = b;
}
}
unsigned char *quickSortHelper(unsigned char *startPtr, unsigned char *endPtr) //找基准数 划分
{
unsigned char *i = startPtr + 3;
unsigned char *j = endPtr;
while (i < j)
{
while (checkValue(startPtr, i))
{
i += 3;
}
while (checkValue(j, startPtr))
{
j -= 3;
}
if (i < j)
{
swapValue(i, j);
i += 3;
j -= 3;
}
else
{
j = i - 3;
}
}
swapValue(i, startPtr);
return i;
}
int quickSort(unsigned char *startPtr, unsigned char *endPtr)
{
if (startPtr >= endPtr)
{
return 1;
}
unsigned char *midPtr = quickSortHelper(startPtr, endPtr);
return quickSort(startPtr, midPtr - 3) + quickSort(midPtr + 3, endPtr);
}
#pragma endregion
int getCeil(int a, int b)
{
return b > 0 ? ((a / b) + (a % b > 0 ? 1 : 0)) : 0;
}
int getAbs(int a)
{
return a > 0 ? a : -a;
}
// 计算颜色值
unsigned int calColor(int r, int g, int b)
{
return 256 * (256 * r + g) + b;
}
// 存储int数值到char数组中
void setIntValue(unsigned char *ptr, int value)
{
for (int i = 3; i >= 0; i--)
{
ptr[i] = value & 0xFF;
value = value >> 8;
}
}
// 存储裁剪结果
int saveSplitResult(unsigned char *startPtr, int left, int top, int right, int bottom, int width, int height, int size, int limit)
{
// 转换坐标
left *= size;
top *= size;
right = (right + 1) * size;
bottom = (bottom + 1) * size;
right = (right > width ? width : right) - 1;
bottom = (bottom > height ? height : bottom) - 1;
width = right - left + 1;
height = bottom - top + 1;
int count = 0;
int _height = height;
if (height > 0 && limit > 0)
{
_height = getCeil(height, getCeil(height, limit) * 15) * 15; // 进行分割,同时避免出现半像素导致的横线,2 * 2.5 * 3
}
while (height > 0)
{
int pos = count * 6 * 4;
// 存放裁剪结果
// [left, top, right, bottom, width, height]
setIntValue(startPtr + pos, left);
setIntValue(startPtr + pos + 4, top);
setIntValue(startPtr + pos + 8, right);
setIntValue(startPtr + pos + 12, bottom);
setIntValue(startPtr + pos + 16, width);
setIntValue(startPtr + pos + 20, height > _height ? _height : height);
height -= _height;
top += _height;
count++;
}
return count;
}
/**
* 计算单元索引
* @param {unsigned char *} startPtr 数据开始指针
* @param {int} bgR 背景色
* @param {int} bgG 背景色
* @param {int} bgB 背景色
* @param {int} scope 容错值,解决因为压缩导致的颜色偏差
* @param {int} colNum 列数
* @param {int} rowNum 行数
*/
unsigned char *indexImg(unsigned char *startPtr, unsigned char bgR, unsigned char bgG, unsigned char bgB, int scope, int colNum, int rowNum)
{
// 单元格大小
int colSize = 4;
// 索引指针
int pos = 0;
// 数据指针
int dataPos = 0;
for (int rowIndex = 0; rowIndex < rowNum; rowIndex++)
{
int rowPos = pos;
bool rowEmpty = true;
for (int colIndex = 0; colIndex < colNum; colIndex++)
{
if (startPtr[dataPos + 3] != ACT_ITEM)
{
// 过滤掉背景色单元格
if (
(getAbs(startPtr[dataPos] - bgR) > scope) ||
(getAbs(startPtr[dataPos + 1] - bgG) > scope) ||
(getAbs(startPtr[dataPos + 2] - bgB) > scope))
{
if (rowEmpty)
{
rowEmpty = false;
}
startPtr[pos] = ACT_ITEM;
}
else
{
// 标记删除该单元格
startPtr[pos] = COL_DEL;
}
}
else
{
if (rowEmpty)
{
rowEmpty = false;
}
startPtr[pos] = ACT_ITEM;
}
pos++;
dataPos += 4;
}
// 标记空行
if (rowEmpty)
{
startPtr[rowPos] = ROW_DEL;
}
}
return startPtr + pos;
}
/**
* 分割图片
* @param {unsigned char *} startPtr 数据开始指针
* @param {unsigned char *} memPtr 当前指针
* @param {int} colNum 列数
* @param {int} leftIndex 左边界
* @param {int} topIndex 上边界
* @param {int} rightIndex 右边界
* @param {int} bottomIndex 下边界
* @param {int} width 图片宽度
* @param {int} height 图片高度
* @param {int} size 识别精度
* @param {int} limit 切片高度,超过这个值会进行分割,值小于0时不进行切片
* @return {int} 分割出的图片个数
*/
int splitImg(unsigned char *startPtr, unsigned char *memPtr, int colNum, int leftIndex, int topIndex, int rightIndex, int bottomIndex, int width, int height, int size, int limit)
{
int top = -1;
int left = -1;
int right = -1;
int bottom = -1;
int count = 0;
unsigned char *rowStartPtr = startPtr + topIndex * colNum;
for (int rowIndex = topIndex; rowIndex <= bottomIndex; rowIndex++)
{
bool rowEmpty = rowStartPtr[0] == ROW_DEL;
// 找出空行
if (!rowEmpty)
{
rowEmpty = true;
for (int colIndex = leftIndex; colIndex <= rightIndex; colIndex++)
{
if (rowStartPtr[colIndex] == ACT_ITEM)
{
if (colIndex < left || left == -1)
{
left = colIndex;
}
if (colIndex > right)
{
right = colIndex;
}
rowEmpty = false;
}
}
}
// 发现空行或匹配到最后一行时,对之前匹配到的区域进行垂直分割
if (rowEmpty || rowIndex == bottomIndex)
{
// 记录被分割的区域
if (top >= 0)
{
if (!rowEmpty)
{
bottom = rowIndex;
}
// 找出空列
bool colEmpty;
int _top = bottom;
int _bottom = top;
int _right;
int _left = left;
// 垂直分割
unsigned char *topRowPtr = startPtr + top * colNum;
unsigned char *_rowStartPtr;
for (int x = left; x <= right; x++)
{
colEmpty = true;
_rowStartPtr = topRowPtr;
for (int y = top; y <= bottom; y++)
{
if (_rowStartPtr[0] != ROW_DEL && _rowStartPtr[x] == ACT_ITEM)
{
if (y < _top)
{
_top = y;
}
if (y > _bottom)
{
_bottom = y;
}
colEmpty = false;
}
_rowStartPtr += colNum;
}
// 发现空列或已经匹配到最后一列时,判断是否有可以切割的图片,有的话将其添加到队列中
if (colEmpty || x == right)
{
_right = colEmpty ? x - 1 : right;
if (_bottom >= _top && _right >= _left)
{
// 如果匹配到的区域还存在继续分割的可能,就采用递归的方式继续进行匹配
if (_top != topIndex || _bottom != bottomIndex || _left != leftIndex || _right != rightIndex)
{
count += splitImg(startPtr, memPtr + count * 4 * 6, colNum, _left, _top, _right, _bottom, width, height, size, limit);
}
else
{
// 存储结果
count += saveSplitResult(memPtr + count * 4 * 6, _left, _top, _right, _bottom, width, height, size, limit);
}
// 恢复指针,开始下一轮匹配
_top = bottom;
_bottom = top;
}
// _left指针移动到下一列
_left = x + 1;
}
}
top = -1;
left = -1;
right = -1;
bottom = -1;
}
}
else
{
if (rowIndex < top || top == -1)
{
top = rowIndex;
}
if (rowIndex > bottom)
{
bottom = rowIndex;
}
}
// 移动指针到下一行起始位置
rowStartPtr += colNum;
}
return count;
}
unsigned int getBgcolor(unsigned char *startPtr, unsigned char *endPtr)
{
unsigned char *ptr = endPtr;
unsigned char *pos;
// 将纯色单元格的颜色值放在数据末尾
for (pos = startPtr; pos < endPtr; pos += 4)
{
if (pos[3] == 2)
{
ptr[0] = pos[0];
ptr[1] = pos[1];
ptr[2] = pos[2];
ptr += 3;
}
}
// 排序
quickSort(endPtr, ptr - 3);
// 遍历找出数量最多的那个颜色
unsigned char maxR;
unsigned char maxG;
unsigned char maxB;
unsigned char lastR = endPtr[0];
unsigned char lastG = endPtr[1];
unsigned char lastB = endPtr[2];
int lastCount = 0;
int maxCount = 0;
for (pos = endPtr; pos < ptr; pos += 3)
{
if (lastR == pos[0] && lastG == pos[1] && lastB == pos[2])
{
lastCount++;
}
else
{
if (maxCount < lastCount)
{
maxR = lastR;
maxG = lastG;
maxB = lastB;
maxCount = lastCount;
// jsLog(lastCount);
}
lastR = pos[0];
lastG = pos[1];
lastB = pos[2];
lastCount = 1;
}
}
if (maxCount < lastCount)
{
maxR = lastR;
maxG = lastG;
maxB = lastB;
}
return ((maxR * 256) + maxG) * 256 + maxB;
}
#pragma endregion
#pragma region 提供给js的方法
extern "C"
{
/**
* @param {Image|Canvas} img 图片
* @param {unsigned char *} startPtr 内存空间
* @param {int} width 图片宽度
* @param {int} height 图片高度
* @param {int} size 识别精度
* @param {int} limit 切片高度,超过这个值会进行分割,值小于0时不进行切片
* @param {int} scope 容错值,解决因为压缩导致的颜色偏差
* @return {unsigned char *} 结果数据指针
*/
unsigned char *analyse(unsigned char *startPtr, int width, int height, int size, int limit, int scope)
{
// 图片数据长度
int dataSize = width * height * 4;
// 一行对应的数据长度
int lineSize = width * 4;
int s1 = size * lineSize;
int s2 = size * 4;
// 计算索引
int w = getCeil(width, size);
int h = getCeil(height, size);
// 图像数据只依次遍历一次,因此可以将索引信息直接覆盖在上面
unsigned char *memPtr = startPtr;
// 计算纯色单元索引 getCeil(width, size) * getCeil(height, size) * 4
for (int y = 0, p1 = 0, iy = 0; y < height; y += size, p1 += s1, iy++)
{
for (int x = 0, p2 = p1, ix = 0; x < width; x += size, p2 += s2, ix++)
{
int size_w = size > width - x ? width - x : size;
int size_h = size > height - y ? height - y : size;
// 统计方格内颜色值
for (int i = 0, p3 = p2; i < size_h; i++, p3 += lineSize)
{
for (int j = 0, p4 = p3; j < size_w; j++, p4 += 4)
{
unsigned char r = startPtr[p4];
unsigned char g = startPtr[p4 + 1];
unsigned char b = startPtr[p4 + 2];
if (memPtr[3] != BG_ITEM)
{
memPtr[0] = r;
memPtr[1] = g;
memPtr[2] = b;
memPtr[3] = BG_ITEM;
}
else if (
(getAbs(memPtr[0] - r) > scope) ||
(getAbs(memPtr[1] - g) > scope) ||
(getAbs(memPtr[2] - b) > scope))
{ // 颜色不一致,跳出循环
memPtr[3] = ACT_ITEM;
i = size_h;
j = size_w;
}
}
}
memPtr += 4;
}
}
// 判断背景色
int backgroundColorValue = getBgcolor(startPtr, memPtr);
unsigned char bgR = backgroundColorValue >> 16;
unsigned char bgG = (backgroundColorValue >> 8) % 256;
unsigned char bgB = backgroundColorValue % 256;
// 计算索引
memPtr = indexImg(startPtr, bgR, bgG, bgB, scope, w, h);
// 分割图片
int count = splitImg(startPtr, memPtr + 7, w, 0, 0, w - 1, h - 1, width, height, size, limit);
// [0, 3) 背景色 R G B
// [3, 7) 裁剪结果数量
// [7, 7 + 24n) 裁剪结果列表 left top right bottom width height
memPtr[0] = bgR;
memPtr[1] = bgG;
memPtr[2] = bgB;
setIntValue(memPtr + 3, count);
return memPtr;
}
}
#pragma endregion