1. 程式人生 > >純手寫JavaScript手寫數字識別?

純手寫JavaScript手寫數字識別?

先貼個原始碼然後睡覺,有事後面再說吧! `<!DOCTYPE html>

<html lang="zh-cn">

<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>手寫體數字識別</title> <style> canvas { outline: 1px solid #000; } </style> </head>

<body> <div style="float:left;padding: 20px"> <!-- 手寫數字畫板 --> <canvas width="640" height="480" id="canvas"></canvas> <p> <button id="clear" style="float: left;">清空畫布</button> <button id="start_count" style="float: right;">開始計算</button> </p> </div> <div style="float: left;padding: 20px;width: 50%;"> <!-- 預處理後的圖片展示 --> <div> <canvas width="28" height="28" id="exhibition_booth" style="width: 256px;height:256px;"> </canvas> <span style="font-size: 300px;padding-left: 50px" id="result_box"></span> </div> <p> <div>預測結果:</div> <div id="result_box"></div> </p> <p> <p><input type="text" id="input"></p> <p><button id="btn">提交</button></p> </p> <p> <div>預測結果:</div> <div> <table> <thead> <tr> <th width="300">結果</th> <th width="500">概率</th> </tr> </thead> <tbody id="tb"> </tbody> </table> </div> </p> </div> </body> <script> //獲取手寫數字畫板 const canvas = document.getElementById('canvas'); const cact = canvas.getContext('2d'); const canvasWidth = 640; const canvasHeight = 480; cact.lineWidth = 30; cact.lineJoin = 'round'; cact.imageSmoothingEnabled = false;

//獲取與處理結果輸出畫板
const exhibition_booth = document.getElementById('exhibition_booth');
const ebct = exhibition_booth.getContext('2d');
ebct.imageSmoothingEnabled = false;

const start_count = document.getElementById('start_count');
const data_box = document.getElementById('data_box');

const input = document.getElementById('input');
const btn = document.getElementById('btn');
const result_box = document.getElementById('result_box');
const tb = document.getElementById('tb');

let dataAggregation = {};
let studyCountAggregation = {};

let nowData = null;

input.onfocus = function () {
    input.value = '';
}

start_count.onclick = function () {
    result_box.innerText = '';
    //清空小畫布
    ebct.clearRect(0, 0, 28, 28);
    let { data } = cact.getImageData(0, 0, canvasWidth, canvasHeight);
    let minX = Infinity, minY = Infinity, maxX = 0, maxY = 0;
    //計算圖片四個邊沿的尺寸
    for (let i = 0; i < data.length; i += 3) {
        let nowPixel = data[i];
        if (nowPixel) {
            const nowX = (i + 1) / 4 % canvasWidth;
            const nowY = ((i + 1) / 4) / canvasWidth;
            if (nowX < minX) {
                minX = nowX;
            }
            if (nowY < minY) {
                minY = nowY;
            }
            if (nowX > maxX) {
                maxX = nowX;
            }
            if (nowY > maxY) {
                maxY = nowY;
            }
        }
    }

    let imgX = Math.ceil(minX);
    let imgY = Math.ceil(minY);
    let imgWidth = Math.ceil(maxX - minX);
    let imgHeight = Math.ceil(maxY - minY);
    let diff = Math.abs(imgWidth - imgHeight);

    if (diff) {
        if (imgWidth > imgHeight) {
            imgY -= diff / 2;
            imgHeight += diff;
        } else {
            imgX -= diff / 2;
            imgWidth += diff;
        }
    }

    // cact.lineWidth = 1;
    // cact.strokeRect(imgX, imgY, imgWidth, imgHeight);
    // cact.lineWidth = 30;
    //把大畫布上的圖片縮放之後複製到小畫布上
    ebct.drawImage(canvas, imgX, imgY, imgWidth, imgHeight, 0, 0, 28, 28);
    //獲取小畫布上的所有畫素點
    ({ data } = ebct.getImageData(0, 0, 28, 28));
    //展開成784個元素的一維陣列(28*28的寬度)
    let list = [];
    for (let i = 0; i < data.length; i += 4) {
        let nowPixel = data[i - 1];
        if (nowPixel) {
            list.push(1);
        } else {
            list.push(0);
        }
    }
    nowData = list;
    return doForecast(list);
}

btn.onclick = function () {
    const text = input.value;
    if (nowData) {
        doStudy(text, nowData);
        start_count.onclick();
        let { result, probability } = doForecast(nowData);
        let i = 0;
        while (result != text) {
            ({ result, probability } = doForecast(nowData));
            i++;
            if (i >= 100) {
                alert('100次都學不會,還學個雞兒!')
                break
            };
        }
        console.log(result);
    }
}

function doForecast(list) {
    let i = 0;
    let forecastResult = [];
    let probabilitySum = 0;
    let additionalProbability = 0;
    for (let result in dataAggregation) {
        let data = dataAggregation[result];
        let num = 0;
        for (let i = 0; i < list.length; i++) {
            let score = -(data[i] / studyCountAggregation[result]);
            if (list[i] == 0) {
                num += score;
            } else {
                num -= score;
            }
        }
        let probability = (num / 784) * 100;
        if (probability < 0) {
            additionalProbability -= probability;
            probability = 0;
        }
        if (probability > 100) {
            probability = 100;
        }
        forecastResult.push({
            'result': result
            , 'probability': probability
        });
        probabilitySum += probability;
        i++;
    }

    forecastResult.sort(function (a, b) {
        return b.probability - a.probability;
    });
    tb.innerHTML = '';
    forecastResult.forEach(function (data) {
        const result = data.result;
        let probability = data.probability;
        const tr = document.createElement('tr');
        const td1 = document.createElement('td');
        const td2 = document.createElement('td');
        td1.innerText = result;
        td2.innerText = probability + '%';
        tr.appendChild(td1);
        tr.appendChild(td2);
        tb.appendChild(tr);
    })
    let result, probability;
    if (forecastResult[0]) {
        result = forecastResult[0].result;
        probability = forecastResult[0].probability
    }
    if (result) {
        result_box.innerText = result;
    }
    if (i == 0) {
        alert('資料集空空如也,請告訴我這是什麼!');
    }
    return { result, probability };
}


function doStudy(result, data) {
    if (result in dataAggregation) {
        const data2 = dataAggregation[result];
        if (data2.length !== data.length) {
            alert('資料長度不一致')
            return;
        }
        studyCountAggregation[result]++;
        for (let i = 0; i < data.length; i++) {
            if (data[i]) {
                data2[i] = data2[i] + 1;
            } else {
                data2[i] = data2[i] - 1;
            }
        }
    } else {
        dataAggregation[result] = [];
        studyCountAggregation[result] = 1;
        for (let i = 0; i < data.length; i++) {
            if (data[i]) {
                dataAggregation[result].push(1);
            } else {
                dataAggregation[result].push(-1);
            }
        }
    }

    for (let key in dataAggregation) {
        if (key == result) continue;
        const data3 = dataAggregation[key];
        for (let i = 0; i < data.length; i++) {
            if (data[i]) {
                data3[i] = data3[i] - 0.05;
            } else {
                data3[i] = data3[i] + 0.05;
            }
        }
        studyCountAggregation[key] += 0.05;
    }
}



//清除按鈕,點選清空畫板
const clearBtn = document.getElementById('clear');
clearBtn.onclick = function () {
    tb.innerHTML = '';
    result_box.innerText = '';
    cact.clearRect(0, 0, 640, 480);
    ebct.clearRect(0, 0, 640, 480);
}








//下面是手寫畫板的事件繫結
let startPainting = false;
canvas.onmousedown = function (e) {
    const { offsetX, offsetY } = e;
    cact.beginPath();
    cact.moveTo(offsetX, offsetY);
    startPainting = true;
}
let lastMouseCoordinate = {
    offsetX: 0
    , offsetY: 0
}
canvas.onmousemove = function (e) {
    const { offsetX, offsetY } = e;
    if (startPainting) {
        const { offsetX: lastX, offsetY: lastY } = lastMouseCoordinate;
        if (lastX + lastY) {
            cact.moveTo(lastX, lastY);
        }
        lastMouseCoordinate = { offsetX, offsetY }
        cact.lineTo(offsetX, offsetY);
        cact.closePath();
        cact.stroke();
    }
}
canvas.onmouseup = function (e) {
    const { offsetX, offsetY } = e;
    startPainting = false
    lastMouseCoordinate = {
        offsetX: 0
        , offsetY: 0
    }
}
document.onmousemove = function (e) {
    if (e.target != canvas) {
        canvas.onmouseup.call(this, e);
    }
}

</script><