|
我没有测试下面的代码,给你参考,你可以多给几组图给ai,这样会更准确的识别出所有这种规律的图

- /**
- * 识别由 '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[r][c]||'-') === fg)
- );
- return {grid, h, w};
- }
- // 把整幅图按“全空白列”切成若干数字列区间
- function splitDigits(grid) {
- const h = grid.length, w = grid[0].length;
- const colHasInk = c => grid.some(row => row[c]);
- const segments = [];
- let inRun = false, start = 0;
- for (let c=0;c
- if (colHasInk(c)) {
- if (!inRun){ inRun = true; start = c; }
- } else {
- if (inRun){
- segments.push([start, c-1]);
- inRun = false;
- }
- }
- }
- if (inRun) segments.push([start, w-1]);
- // 去掉过窄的碎片(噪声)
- return segments.filter(([s,e]) => e - s + 1 >= 2);
- }
- // 从列区间裁切出单个数字的像素矩阵,并上下裁边(去掉全背景的行)
- function cropDigit(grid, colStart, colEnd) {
- const h = grid.length;
- let rTop = 0, rBot = h-1;
- // 找最上方含墨的行
- while (rTop < h && grid[rTop].slice(colStart, colEnd+1).every(v=>!v)) rTop++;
- // 找最下方含墨的行
- while (rBot >= 0 && grid[rBot].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[r].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[0].length-1));
- c1 = Math.max(0, Math.min(c1, board[0].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[r][c]) 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[0].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[0]<<6)|(bits[1]<<5)|(bits[2]<<4)|(bits[3]<<3)|(bits[4]<<2)|(bits[5]<<1)|(bits[6]<<0);
- }
- // 标准七段到数字的映射(常见数码管编码)
- const sevenSegToDigit = new Map([
- // a b c d e f g
- [0b1110111, 0], // 0: a b c d e f (g=0) -> 0b1110111?(按位需核对下顺序)
- [0b0100100, 1], // 1: b c
- [0b1011101, 2], // 2: a b d e g
- [0b1101101, 3], // 3: a b c d g
- [0b0101110, 4], // 4: b c f g
- [0b1101011, 5], // 5: a c d f g
- [0b1111011, 6], // 6: a c d e f g
- [0b0100101, 7], // 7: a b c(有的实现不亮 g)
- [0b1111111, 8], // 8: all
- [0b1101111, 9], // 9: a b c d f g
- ]);
- // 由于上面的位序容易混淆,提供一个更稳妥的映射构造器:用[a,b,c,d,e,f,g]布尔数组生成mask
- function maskFromSegs([a,b,c,d,e,f,g]) {
- return (a<<6)|(b<<5)|(c<<4)|(d<<3)|(e<<2)|(f<<1)|(g<<0);
- }
- // 用布尔数组重建映射,避免手写二进制出错
- const LUT = new Map([
- [maskFromSegs([1,1,1,1,1,1,0]), 0],
- [maskFromSegs([0,1,1,0,0,0,0]), 1],
- [maskFromSegs([1,1,0,1,1,0,1]), 2],
- [maskFromSegs([1,1,1,1,0,0,1]), 3],
- [maskFromSegs([0,1,1,0,0,1,1]), 4],
- [maskFromSegs([1,0,1,1,0,1,1]), 5],
- [maskFromSegs([1,0,1,1,1,1,1]), 6],
- [maskFromSegs([1,1,1,0,0,0,0]), 7],
- [maskFromSegs([1,1,1,1,1,1,1]), 8],
- [maskFromSegs([1,1,1,1,0,1,1]), 9],
- ]);
- function recognizeAsciiDigits(art, fg='8') {
- const {grid} = parseArt(art, fg);
- const segments = splitDigits(grid);
- const digits = [];
- for (const [cs,ce] 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
复制代码
|
|