求教:如何识别字串组成的数字
------------------------------------------888-----888-----888-----888-----888---
-8---8---8---8---8---8---8---8---8---8--
-----8-------8---8---8---8---8-------8--
----8------88----8---8---8---8------8---
---8---------8---8---8---8---8-----8----
--8----------8---8---8---8---8----8-----
-8---8---8---8---8---8---8---8---8---8--
-88888----888-----888-----888----88888--
----------------------------------------
如图数字是23002,但怎么让机器识别呢?
我没有测试下面的代码,给你参考,你可以多给几组图给ai,这样会更准确的识别出所有这种规律的图
data/attachment/forum/b8125d67df27bb813fe50b3685761e25.png
/**
* 识别由 '8'(前景)和 '-'(背景)构成的 ASCII 数字横排。
* 核心:按空列把整幅图切成单个数字,然后用七段数码管的7个区域做密度判定。
*/
function parseArt(art, fg='8') {
const lines = art.replace(/\r/g,'').split('\n').filter(l=>l.trim().length>0);
const h = lines.length;
const w = Math.max(...lines.map(l=>l.length));
// 右侧用背景补齐,保证等宽
const grid = Array.from({length:h}, (_,r)=>
Array.from({length:w}, (_,c)=> (lines||'-') === fg)
);
return {grid, h, w};
}
// 把整幅图按“全空白列”切成若干数字列区间
function splitDigits(grid) {
const h = grid.length, w = grid.length;
const colHasInk = c => grid.some(row => row);
const segments = [];
let inRun = false, start = 0;
for (let c=0;c<w;c++){
if (colHasInk(c)) {
if (!inRun){ inRun = true; start = c; }
} else {
if (inRun){
segments.push();
inRun = false;
}
}
}
if (inRun) segments.push();
// 去掉过窄的碎片(噪声)
return segments.filter(() => e - s + 1 >= 2);
}
// 从列区间裁切出单个数字的像素矩阵,并上下裁边(去掉全背景的行)
function cropDigit(grid, colStart, colEnd) {
const h = grid.length;
let rTop = 0, rBot = h-1;
// 找最上方含墨的行
while (rTop < h && grid.slice(colStart, colEnd+1).every(v=>!v)) rTop++;
// 找最下方含墨的行
while (rBot >= 0 && grid.slice(colStart, colEnd+1).every(v=>!v)) rBot--;
if (rTop > rBot) return null;
const sub = [];
for (let r=rTop;r<=rBot;r++){
sub.push(grid.slice(colStart, colEnd+1));
}
return sub;
}
// 计算某个区域的“点亮密度”(true 比例)
function density(board, r0, r1, c0, c1) {
r0 = Math.max(0, Math.min(r0, board.length-1));
r1 = Math.max(0, Math.min(r1, board.length-1));
c0 = Math.max(0, Math.min(c0, board.length-1));
c1 = Math.max(0, Math.min(c1, board.length-1));
if (r1 < r0 || c1 < c0) return 0;
let cnt=0, tot=0;
for (let r=r0;r<=r1;r++){
for (let c=c0;c<=c1;c++){
tot++;
if (board) cnt++;
}
}
return tot ? cnt/tot : 0;
}
/**
* 把一个数字块用“七段”来判定:a,b,c,d,e,f,g
*a: 顶横b: 右上c: 右下d: 底横e: 左下f: 左上g: 中横
* 返回 bitmask(按 abcdefg -> 6..0 位)
*/
function sevenSegMask(board) {
const H = board.length, W = board.length;
// 为了鲁棒,区域占整体的一定比例,适配粗细不同
const th = Math.max(1, Math.floor(H * 0.18)); // 横段厚度
const tv = Math.max(1, Math.floor(W * 0.18)); // 竖段厚度
const pad = Math.max(1, Math.floor(Math.min(H,W) * 0.08)); // 边缘留白
const midR = Math.floor(H/2);
// 各段的采样窗口
const a = density(board, pad, pad+th-1, pad, W-1-pad);
const d = density(board, H-1-pad-th+1, H-1-pad, pad, W-1-pad);
const g = density(board, midR - Math.floor(th/2), midR + Math.ceil(th/2)-1, pad, W-1-pad);
const f = density(board, pad, midR - Math.ceil(th/2), pad, pad+tv-1);
const b = density(board, pad, midR - Math.ceil(th/2), W-1-pad-tv+1, W-1-pad);
const e = density(board, midR + Math.floor(th/2), H-1-pad, pad, pad+tv-1);
const c = density(board, midR + Math.floor(th/2), H-1-pad, W-1-pad-tv+1, W-1-pad);
// 阈值:段区域内有一定比例的 '8' 就认为该段点亮
const T = 0.35; // 可按需要微调
const bit = v => v >= T ? 1 : 0;
const bits = [
bit(a), // a
bit(b), // b
bit(c), // c
bit(d), // d
bit(e), // e
bit(f), // f
bit(g)// g
];
// 组装成掩码(abc def g -> 6..0)
return (bits<<6)|(bits<<5)|(bits<<4)|(bits<<3)|(bits<<2)|(bits<<1)|(bits<<0);
}
// 标准七段到数字的映射(常见数码管编码)
const sevenSegToDigit = new Map([
// a b c d e f g
, // 0: a b c d e f (g=0) -> 0b1110111?(按位需核对下顺序)
, // 1: b c
, // 2: a b d e g
, // 3: a b c d g
, // 4: b c f g
, // 5: a c d f g
, // 6: a c d e f g
, // 7: a b c(有的实现不亮 g)
, // 8: all
, // 9: a b c d f g
]);
// 由于上面的位序容易混淆,提供一个更稳妥的映射构造器:用布尔数组生成mask
function maskFromSegs() {
return (a<<6)|(b<<5)|(c<<4)|(d<<3)|(e<<2)|(f<<1)|(g<<0);
}
// 用布尔数组重建映射,避免手写二进制出错
const LUT = new Map([
), 0],
), 1],
), 2],
), 3],
), 4],
), 5],
), 6],
), 7],
), 8],
), 9],
]);
function recognizeAsciiDigits(art, fg='8') {
const {grid} = parseArt(art, fg);
const segments = splitDigits(grid);
const digits = [];
for (const of segments) {
const block = cropDigit(grid, cs, ce);
if (!block) { digits.push('?'); continue; }
const mask = sevenSegMask(block);
const d = LUT.get(mask);
digits.push(d !== undefined ? String(d) : '?');
}
return digits.join('');
}
// ------------------ DEMO ------------------
const art = `
----------------------------------------
--888-----888-----888-----888-----888---
-8---8---8---8---8---8---8---8---8---8--
-----8-------8---8---8---8---8-------8--
----8------88----8---8---8---8------8---
---8---------8---8---8---8---8-----8----
--8----------8---8---8---8---8----8-----
-8---8---8---8---8---8---8---8---8---8--
-88888----888-----888-----888----88888--
----------------------------------------
`;
console.log(recognizeAsciiDigits(art)); // 期望输出: 23002
非常感谢! 分块,标准化,对比。
分块是从指定位置把待识别的图案裁出来。
标准化是把背景替换为标准字符,前景替换为标准字符,让后转成一个字符串。
对比是在事先准备好的字符串字典里找到对应的值。 这不就是简单的点阵字吗
页:
[1]