Basic Image recognition (Javascript)

Date: 2018-11-09
<html>

<body>

    <canvas id="canvas" width="600" height="450" style="float:left"></canvas>
    <canvas id="out" width="600" height="450" style="float:left"></canvas>
    <script>
        let img = new Image();
        img.src = 'lines1.png';
        let canvas = document.getElementById('canvas');
        let ctx = canvas.getContext('2d');
        let out = document.getElementById('out');
        let ctxOut = out.getContext('2d');

        img.onload = function() {
            ctx.drawImage(img, 0, 0);
            img.style.display = 'none';

            let start = new Date();
            drawRanges();
            let end = new Date();
            console.log('time', end.getTime() - start.getTime());
        };

        function rangeLen(range) {
            return Math.sqrt(
                (range.x2 - range.x1) * (range.x2 - range.x1) +
                (range.y2 - range.y1) * (range.y2 - range.y1)
            );
        };

        let unique = (array) => {
            return Array.from(new Set(array));
        };

        let merge = (arrays) => {
            return [].concat.apply([], arrays);
        };

        let rangeOverlap = (min1, max1, min2, max2) => {
            if (min1 > max1 || min2 > max2) {
                throw new Error("Invalid range");
            }
            if (max2 <= min1 || min2 >= max1) {
                return 0;
            }
            if (min1 <= min2 && max1 >= max2) {
                return max2 - min2;
            }
            if (min1 >= min2 && max1 <= max2) {
                return max1 - min1;
            }
            if (min1 >= min2 && max1 >= max2 && max2 >= min1) {
                return max2 - min1;
            }
            if (min1 <= min2 && max1 >= min2 && max2 >= max1) {
                return max1 - min2;
            }
            return 0;
        };

        let rangeCollidesWithRanges = (range, ranges) => {
            let results = [];
            for (var i = 0; i < ranges.length; i++) {
                let overlap = rangeOverlap(range.x1, range.x2, ranges[i].x1, ranges[i].x2);
                if (overlap > 0) {
                    results.push(ranges[i]);
                }
            }
            return results;
        };

        function getRanges(data, width, height) {
            let inRange = false;
            let ranges = [];
            let range = null;

            let prevRowRanges = [];
            let rowRanges = [];
            let bodies = [];
            let cnt = 0;

            let startRange = (x, y) => {
                return {
                    x1: x,
                    y1: y,
                    x2: 0,
                    y2: 0
                };
            };

            let endRange = (range, x, y) => {
                range.x2 = x;
                range.y2 = y;
                ranges.push(range);
                rowRanges.push(range);

                let cols = rangeCollidesWithRanges(range, prevRowRanges);
                if (cols.length == 0) {
                    range.bodies = [cnt];
                    bodies.push(cnt);
                    cnt += 1;
                } else {
                    range.bodies = unique(merge(cols.map((col) => col.bodies)));
                }

                return null;
            };

            let doPixel = (x, y, r, g, b, a) => {
                // pixel filter here
                inRange = r < 200;
                if (inRange) {
                    if (!range) {
                        range = startRange(x, y);
                    }
                } else {
                    if (range) {
                        range = endRange(range, x, y);
                    }
                }

                if (y + 1 == height) {
                    if (range) {
                        range = endRange(range, x, y);
                    }
                }
            };

            for (let y = 0; y < height; y += 1) {
                prevRowRanges = rowRanges;
                rowRanges = [];

                for (let x = 0; x < width; x += 1) {
                    let p = (y * width * 4) + (x * 4);
                    doPixel(x, y, data[p], data[p + 1], data[p + 2], data[p + 3]);
                }
            }
            return {
                ranges: ranges,
                bodies: bodies
            };
        }

        function drawRange(ctx, range) {
            ctx.beginPath();
            ctx.moveTo(range.x1, range.y1);
            ctx.lineTo(range.x2, range.y2);
            ctx.stroke();
        }

        function circle(ctx, x,y,r)
        {
            ctxOut.beginPath();
            ctxOut.strokeStyle = 'black';
            ctx.arc(x, y, r, 0, 2 * Math.PI);
            ctxOut.stroke();
        }

        function drawRanges() {
            let width = canvas.width;
            let height = canvas.height;
            let data = ctx.getImageData(0, 0, width, height);
            let rangesAndBodies = getRanges(data.data, data.width, data.height);

            let ranges = rangesAndBodies.ranges;
            let bodies = rangesAndBodies.bodies;

            let colors = ['red', 'yellow', 'green', 'magenta', 'teal', 'purple', 'blue', 'navy', 'orange'];
            //console.log('ranges', rangesAndBodies);

            ctxOut.lineWidth = 1;

            for (var i = 0; i < bodies.length; i++) {
                var b = bodies[i];

                let bodyRanges = ranges.filter((range) => {
                    return range.bodies.indexOf(b) >= 0;
                });

                ctxOut.strokeStyle = colors[i % colors.length];

                bodyRanges.forEach(function(range, i) {
                    drawRange(ctxOut, range);
                });

                let minXy = bodyRanges.reduce(function(r1, r2) {
                    return (r1.x1 < r2.x1 ? r1 : r2);
                });
                let minYx = bodyRanges.reduce(function(r1, r2) {
                    return (r1.y1 < r2.y1 ? r1 : r2);
                });
                let maxXy = bodyRanges.reduce(function(r1, r2) {
                    return (r1.x2 > r2.x2 ? r1 : r2);
                });
                let maxYx = bodyRanges.reduce(function(r1, r2) {
                    return (r1.y2 > r2.y2 ? r1 : r2);
                });

                circle(ctxOut, minXy.x1,minXy.y1, 3);
                circle(ctxOut, minYx.x1,minYx.y1, 3);
                circle(ctxOut, maxXy.x2,maxXy.y2, 3);
                circle(ctxOut, maxYx.x2,maxYx.y2, 3);

                //console.log('rect', minX,minY,maxX-minX,maxY-minY);

                ctxOut.beginPath();
                ctxOut.strokeStyle = colors[(i + 2) % colors.length];
                ctxOut.rect(minXy.x1, minYx.y1, maxXy.x2 - minXy.x1, maxYx.y2 - minYx.y1);
                ctxOut.stroke();
            }
        }
    </script>
</body>

</html>
7500cookie-checkBasic Image recognition (Javascript)