识别快递单号(3)

 上篇: 加载图片到canvas 说到使用javascript处理选中的图片, 下面就是见证Demo的时刻:

  转载请注明出处: http://www.cnblogs.com/zaiyuzhong/p/change-the-flow.html

 Demo

    <input type="file" id="upfile" /><br/>非图片文件将不会被处理    
    <br/>
    <script>
        var iCanvas = document.createElement('canvas'),
            iCtx = iCanvas.getContext('2d');
        var test;
        function iResize(width, height) {
            iCanvas.width = width;
            iCanvas.height = height;
        }

        function Mat(row, col, data, buffer) {
            this.row = row || 0;
            this.col = col || 0;
            this.channel = 4;
            this.buffer = buffer || new ArrayBuffer(row * col * this.channel);
            this.data = new Uint8ClampedArray(this.buffer);
            data && this.data.set(data);
            this.bytes = 1;
            this.type = 'CV_RGBA';
        }

        function imgRead(image) {
            var width = image.width,
                height = image.height;
            iResize(width, height);
            iCtx.drawImage(image, 0, 0);
            var imageData = iCtx.getImageData(0, 0, width, height),
                tempMat = new Mat(height, width, imageData.data);
            imageData = null;
            iCtx.clearRect(0, 0, width, height);
            return tempMat;
        }

        window.onload = function () {
            document.querySelector('#upfile').onchange = function (evt) {
                if (evt.target.files.length < 1) return;
                var file = evt.target.files[0];
                if (!file.type.match('image.*')) return;
                var reader = new FileReader();
                reader.onloadend = function (e) {
                    if (e.target.readyState == FileReader.DONE) {
                        var img = new Image();
                        img.onload = function () {
                            var myMat = imgRead(img);
                            test = myMat;

                            var gray = cvtColor(myMat);
                            var t = GetHistGram(gray.data);
                            var thr = GetYenThreshold(t);
                            BlackOrWhite(gray.data, thr);
                            var imgData = RGBA2ImageData(gray);
                            iCtx.putImageData(imgData, 0, 0);
                            document.body.appendChild(iCanvas);
                        };
                        img.src = e.target.result;
                    }
                };
                reader.readAsDataURL(file);
            };
        };

        function BlackOrWhite(src, threshold) {
            for (var i = 0; i < src.length; i += 4)
                if (src[i] > threshold) src[i] = src[i + 1] = src[i + 2] = 255;
                else src[i] = src[i + 1] = src[i + 2] = 0;
            console.info(src);
        }

        function RGBA2ImageData(imgMat) {
            var width = imgMat.col,
                height = imgMat.row,
                imageData = iCtx.createImageData(width, height);
            imageData.data.set(imgMat.data);
            return imageData;
        }

        function cvtColor(src) {
            if (src.type && src.type === 'CV_RGBA') {
                var row = src.row,
                    col = src.col;
                var dst = new Mat(row, col),
                    data = dst.data,
                    data2 = src.data;
                var pix1, pix2, t,
                    pix = src.row * src.col * src.channel;
                while (pix) {
                    t = parseInt((data2[pix] + data2[pix1] + data2[pix2]) / 3, 10);
                    //t = parseInt((data2[pix] + data2[pix1] + data2[pix2]) / 3 + 0.5, 10);
                    data[pix -= src.channel] = data[pix1 = pix + 1] = data[pix2 = pix + 2] = t;
                    data[pix + 3] = 255;
                }
            } else return src;
            return dst;
        }

        function GetHistGram(src) {
            var histGram = new Int32Array(256);
            for (var i = 0; i < histGram.length; i++) histGram[i] = 0;
            for (var i = 1; i < src.length; i++)
                if (i % 4 == 2) histGram[src[i]]++;
            return histGram;
        }

        function GetYenThreshold(histGram) {
            var ih, it, crit, max_crit,
                norm_histo = new Array(histGram.length),
                P1 = new Array(histGram.length),
                P1_sq = new Array(histGram.length),
                P2_sq = new Array(histGram.length),
                threshold = -1, total = 0;

            for (ih = 0; ih < histGram.length; ih++) total += histGram[ih];
            for (ih = 0; ih < histGram.length; ih++) norm_histo[ih] = histGram[ih] / total;

            P1[0] = norm_histo[0];
            for (ih = 1; ih < histGram.length; ih++) P1[ih] = P1[ih - 1] + norm_histo[ih];

            P1_sq[0] = norm_histo[0] * norm_histo[0];
            for (ih = 1; ih < histGram.length; ih++) P1_sq[ih] = P1_sq[ih - 1] + norm_histo[ih] * norm_histo[ih];

            P2_sq[histGram.length - 1] = 0.0;
            for (ih = histGram.length - 2; ih >= 0; ih--) P2_sq[ih] = P2_sq[ih + 1] + norm_histo[ih + 1] * norm_histo[ih + 1];

            max_crit = -1.0 * ((P1_sq[0] * P2_sq[0]) > 0.0 ? Math.log(P1_sq[0] * P2_sq[0]) : 0.0) + 2 * ((P1[0] * (1.0 - P1[0])) > 0.0 ? Math.log(P1[0] * (1.0 - P1[0])) : 0.0);
            for (it = 1; it < histGram.length; it++) {
                crit = -1.0 * ((P1_sq[it] * P2_sq[it]) > 0.0 ? Math.log(P1_sq[it] * P2_sq[it]) : 0.0) + 2 * ((P1[it] * (1.0 - P1[it])) > 0.0 ? Math.log(P1[it] * (1.0 - P1[it])) : 0.0);
                if (crit > max_crit) {
                    max_crit = crit;
                    threshold = it;
                }
            }
            return threshold;
        }
    </script>   
源码
  这里有个小问题, 我用javascript灰度化图片和C#灰度化的不一样, javascript的值偏小也就是会暗一些, 偏差了4到几十不等, 数值越大偏差越多. 好在是整体的到不影响后面的二值化.

  好了, 现在Android端基本赶上本系列第一篇的进度了, 完成了 点击上传图片->拍照->处理成为黑白图片 这些步骤, 但由于是在浏览器端实现的所以无法在拍照时画一条线来标示二维码位置. 在测试中也发现很多时候竖屏拍照处理后是横屏, 横屏拍照处理后是竖屏的情况. 这样就需要先找到条码位置. 我思考了好几天该如何确定条码在图片中是横着的还是竖着的, 终于我不得不做一个艰难的决定: 放一个图片提示该如何照相, 并做一个把图片竖起来的处理.

  既然这样, 操作流程就需要改了: 先截取图片, 确认后开始处理, 上传结果.
    <input type="file" id="upfile" accept="image/*" /><br />非图片文件将不会被处理, 中间范围将作为识别区域, 如下图:<br />
    <div style="position:relative">
        <canvas style="position:absolute;z-index:10"></canvas>
        <div id="cover" style="position:absolute;height:100%;background-color:rgba(0,0,0,0.5);"></div>
        <img alt="示例图片" src="//images0.cnblogs.com/blog2015/473584/201503/261704169582879.jpg" style="background-color:green" />
    </div>
    <script>
        var iCanvas = document.querySelector('canvas'),
            iCtx = iCanvas.getContext('2d');

        document.querySelector('img').onload = function () {
            document.querySelector('#cover').style.width = this.width + 'px';
            iCanvas.width = this.width;
            iCanvas.height = this.height / 10;
            var h = this.height * 0.4;
            iCanvas.style.top = h + 'px';
            iCtx.drawImage(this, 0, 0 - h);
        };

        window.onload = function () {
            document.querySelector('#upfile').onchange = function (evt) {
                if (evt.target.files.length < 1) return;
                var file = evt.target.files[0];
                if (!file.type.match('image.*')) return;
                var reader = new FileReader();
                reader.onloadend = function (e) {
                    if (e.target.readyState == FileReader.DONE) {
                        var img = document.querySelector('img');
                        img.src = e.target.result;
                    }
                };
                reader.readAsDataURL(file);
            };
        };
  </script>
View Code

  jsbin不能用了, 不给我生成分享链接, 没空研究是怎么回事就先不放Demo了. 说实话不喜欢这样, 不放对位置就不能识别条码, 太不智能了. 我想先把东西做出来然后再改吧.
  好了, 下篇就可以将二维码图片识别成01串, 上传服务器处理了.
原文地址:https://www.cnblogs.com/zaiyuzhong/p/change-the-flow.html