{"id":750,"date":"2018-11-09T13:56:18","date_gmt":"2018-11-09T12:56:18","guid":{"rendered":"https:\/\/solidt.eu\/site\/?p=750"},"modified":"2022-07-25T08:35:44","modified_gmt":"2022-07-25T07:35:44","slug":"basic-image-recognition-javascript","status":"publish","type":"post","link":"https:\/\/solidt.eu\/site\/basic-image-recognition-javascript\/","title":{"rendered":"Basic Image recognition (Javascript)"},"content":{"rendered":"\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;html>\n\n&lt;body>\n\n    &lt;canvas id=\"canvas\" width=\"600\" height=\"450\" style=\"float:left\">&lt;\/canvas>\n    &lt;canvas id=\"out\" width=\"600\" height=\"450\" style=\"float:left\">&lt;\/canvas>\n    &lt;script>\n        let img = new Image();\n        img.src = 'lines1.png';\n        let canvas = document.getElementById('canvas');\n        let ctx = canvas.getContext('2d');\n        let out = document.getElementById('out');\n        let ctxOut = out.getContext('2d');\n\n        img.onload = function() {\n            ctx.drawImage(img, 0, 0);\n            img.style.display = 'none';\n\n            let start = new Date();\n            drawRanges();\n            let end = new Date();\n            console.log('time', end.getTime() - start.getTime());\n        };\n\n        function rangeLen(range) {\n            return Math.sqrt(\n                (range.x2 - range.x1) * (range.x2 - range.x1) +\n                (range.y2 - range.y1) * (range.y2 - range.y1)\n            );\n        };\n\n        let unique = (array) => {\n            return Array.from(new Set(array));\n        };\n\n        let merge = (arrays) => {\n            return [].concat.apply([], arrays);\n        };\n\n        let rangeOverlap = (min1, max1, min2, max2) => {\n            if (min1 > max1 || min2 > max2) {\n                throw new Error(\"Invalid range\");\n            }\n            if (max2 &lt;= min1 || min2 >= max1) {\n                return 0;\n            }\n            if (min1 &lt;= min2 &amp;&amp; max1 >= max2) {\n                return max2 - min2;\n            }\n            if (min1 >= min2 &amp;&amp; max1 &lt;= max2) {\n                return max1 - min1;\n            }\n            if (min1 >= min2 &amp;&amp; max1 >= max2 &amp;&amp; max2 >= min1) {\n                return max2 - min1;\n            }\n            if (min1 &lt;= min2 &amp;&amp; max1 >= min2 &amp;&amp; max2 >= max1) {\n                return max1 - min2;\n            }\n            return 0;\n        };\n\n        let rangeCollidesWithRanges = (range, ranges) => {\n            let results = [];\n            for (var i = 0; i &lt; ranges.length; i++) {\n                let overlap = rangeOverlap(range.x1, range.x2, ranges[i].x1, ranges[i].x2);\n                if (overlap > 0) {\n                    results.push(ranges[i]);\n                }\n            }\n            return results;\n        };\n\n        function getRanges(data, width, height) {\n            let inRange = false;\n            let ranges = [];\n            let range = null;\n\n            let prevRowRanges = [];\n            let rowRanges = [];\n            let bodies = [];\n            let cnt = 0;\n\n            let startRange = (x, y) => {\n                return {\n                    x1: x,\n                    y1: y,\n                    x2: 0,\n                    y2: 0\n                };\n            };\n\n            let endRange = (range, x, y) => {\n                range.x2 = x;\n                range.y2 = y;\n                ranges.push(range);\n                rowRanges.push(range);\n\n                let cols = rangeCollidesWithRanges(range, prevRowRanges);\n                if (cols.length == 0) {\n                    range.bodies = [cnt];\n                    bodies.push(cnt);\n                    cnt += 1;\n                } else {\n                    range.bodies = unique(merge(cols.map((col) => col.bodies)));\n                }\n\n                return null;\n            };\n\n            let doPixel = (x, y, r, g, b, a) => {\n                \/\/ pixel filter here\n                inRange = r &lt; 200;\n                if (inRange) {\n                    if (!range) {\n                        range = startRange(x, y);\n                    }\n                } else {\n                    if (range) {\n                        range = endRange(range, x, y);\n                    }\n                }\n\n                if (y + 1 == height) {\n                    if (range) {\n                        range = endRange(range, x, y);\n                    }\n                }\n            };\n\n            for (let y = 0; y &lt; height; y += 1) {\n                prevRowRanges = rowRanges;\n                rowRanges = [];\n\n                for (let x = 0; x &lt; width; x += 1) {\n                    let p = (y * width * 4) + (x * 4);\n                    doPixel(x, y, data[p], data[p + 1], data[p + 2], data[p + 3]);\n                }\n            }\n            return {\n                ranges: ranges,\n                bodies: bodies\n            };\n        }\n\n        function drawRange(ctx, range) {\n            ctx.beginPath();\n            ctx.moveTo(range.x1, range.y1);\n            ctx.lineTo(range.x2, range.y2);\n            ctx.stroke();\n        }\n\n        function circle(ctx, x,y,r)\n        {\n            ctxOut.beginPath();\n            ctxOut.strokeStyle = 'black';\n            ctx.arc(x, y, r, 0, 2 * Math.PI);\n            ctxOut.stroke();\n        }\n\n        function drawRanges() {\n            let width = canvas.width;\n            let height = canvas.height;\n            let data = ctx.getImageData(0, 0, width, height);\n            let rangesAndBodies = getRanges(data.data, data.width, data.height);\n\n            let ranges = rangesAndBodies.ranges;\n            let bodies = rangesAndBodies.bodies;\n\n            let colors = ['red', 'yellow', 'green', 'magenta', 'teal', 'purple', 'blue', 'navy', 'orange'];\n            \/\/console.log('ranges', rangesAndBodies);\n\n            ctxOut.lineWidth = 1;\n\n            for (var i = 0; i &lt; bodies.length; i++) {\n                var b = bodies[i];\n\n                let bodyRanges = ranges.filter((range) => {\n                    return range.bodies.indexOf(b) >= 0;\n                });\n\n                ctxOut.strokeStyle = colors[i % colors.length];\n\n                bodyRanges.forEach(function(range, i) {\n                    drawRange(ctxOut, range);\n                });\n\n                let minXy = bodyRanges.reduce(function(r1, r2) {\n                    return (r1.x1 &lt; r2.x1 ? r1 : r2);\n                });\n                let minYx = bodyRanges.reduce(function(r1, r2) {\n                    return (r1.y1 &lt; r2.y1 ? r1 : r2);\n                });\n                let maxXy = bodyRanges.reduce(function(r1, r2) {\n                    return (r1.x2 > r2.x2 ? r1 : r2);\n                });\n                let maxYx = bodyRanges.reduce(function(r1, r2) {\n                    return (r1.y2 > r2.y2 ? r1 : r2);\n                });\n\n                circle(ctxOut, minXy.x1,minXy.y1, 3);\n                circle(ctxOut, minYx.x1,minYx.y1, 3);\n                circle(ctxOut, maxXy.x2,maxXy.y2, 3);\n                circle(ctxOut, maxYx.x2,maxYx.y2, 3);\n\n                \/\/console.log('rect', minX,minY,maxX-minX,maxY-minY);\n\n                ctxOut.beginPath();\n                ctxOut.strokeStyle = colors[(i + 2) % colors.length];\n                ctxOut.rect(minXy.x1, minYx.y1, maxXy.x2 - minXy.x1, maxYx.y2 - minYx.y1);\n                ctxOut.stroke();\n            }\n        }\n    &lt;\/script>\n&lt;\/body>\n\n&lt;\/html><\/pre>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[5,4],"tags":[],"class_list":["post-750","post","type-post","status-publish","format-standard","hentry","category-javascript","category-programming"],"_links":{"self":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/750","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/comments?post=750"}],"version-history":[{"count":4,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/750\/revisions"}],"predecessor-version":[{"id":2534,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/750\/revisions\/2534"}],"wp:attachment":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/media?parent=750"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/categories?post=750"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/tags?post=750"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}