From 326dae4acc33b2d1c45b1bcbcba36a0a581e225b Mon Sep 17 00:00:00 2001
From: Yuriy Maksymets <iurii.maksymets@gmail.com>
Date: Sun, 10 Nov 2019 17:19:58 +0000
Subject: [PATCH] Simple Hough transform line detection

---
 __tests__/shape.test.js | 13 +++----
 src/shapes.js           | 75 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 77 insertions(+), 11 deletions(-)

diff --git a/__tests__/shape.test.js b/__tests__/shape.test.js
index c8a2db9..201a159 100644
--- a/__tests__/shape.test.js
+++ b/__tests__/shape.test.js
@@ -13,25 +13,22 @@ describe("shape recognition", () => {
     test("should recognize a simple horizontal line", () => {
       const points = [[0, 0], [100, 0]]
       const result = recognizeFromPoints(points)
-      expect(result).toBe(Shapes.line)
+      expect(result.shape).toBe(Shapes.line)
     })
-
     test("should recognize a simple vertical line", () => {
       const points = [[0, 50], [0, -100]]
       const result = recognizeFromPoints(points)
-      expect(result).toBe(Shapes.line)
+      expect(result.shape).toBe(Shapes.line)
     })
-
     test("should recognize a slightly curve horizontal line", () => {
-      const points = [[0, 0], [30, 10], [100, 2]]
+      const points = [[0, 0], [30, 5], [100, 2]]
       const result = recognizeFromPoints(points)
-      expect(result).toBe(Shapes.line)
+      expect(result.shape).toBe(Shapes.line)
     })
-
     test("should not recognize a really curved horizontal line", () => {
       const points = [[0, 0], [30, 30], [100, -4]]
       const result = recognizeFromPoints(points)
-      expect(result).not.toBe(Shapes.line)
+      expect(result.shape).not.toBe(Shapes.line)
     })
   })
 
diff --git a/src/shapes.js b/src/shapes.js
index 2c827fd..72f8e71 100644
--- a/src/shapes.js
+++ b/src/shapes.js
@@ -1,8 +1,77 @@
+function dtor(a) {
+  return (Math.PI * a) / 180
+}
+function cos(a) {
+  return Math.cos(dtor(a))
+}
+function sin(a) {
+  return Math.sin(dtor(a))
+}
+
+const rhoStep = 5
+const angleStep = 90
+const numAngleCells = 180 / angleStep
+const rhoMax = 1000
+
+function findMaxInHough(accum, threshold) {
+  let max = 0
+  //   let bestRho = 0
+  let bestTheta = 0
+  for (let i = 0; i < numAngleCells; i++) {
+    for (let j = 0; j < accum[i].length; j++) {
+      if (accum[i][j] > max) {
+        max = accum[i][j]
+        // bestRho = j
+        bestTheta = i
+      }
+    }
+  }
+  //   bestRho <<= 1
+  //   bestRho -= rhoMax
+  //   bestRho *= rhoStep
+  bestTheta *= angleStep
+
+  if (max > threshold) {
+    if (bestTheta === 90) {
+      return 90
+    } else {
+      return 0
+    }
+  }
+  return undefined
+}
+
+function constructHoughAccumulator(accumulator, x, y) {
+  for (let thetaIndex = 0; thetaIndex < numAngleCells; thetaIndex++) {
+    const theta = thetaIndex * angleStep
+    let rho = x * cos(theta) + y * sin(theta)
+    rho = Math.floor(rho)
+    rho += rhoMax
+    rho >>= 1
+    rho /= rhoStep
+    rho = Math.floor(rho)
+    if (accumulator[thetaIndex] == undefined) accumulator[thetaIndex] = []
+    if (accumulator[thetaIndex][rho] == undefined) {
+      accumulator[thetaIndex][rho] = 1
+    } else {
+      accumulator[thetaIndex][rho]++
+    }
+  }
+}
+
 function recognizeFromPoints(points) {
-  return {
-    shape: Shapes.rectangle,
-    points,
+  const accum = Array(numAngleCells)
+  points.forEach((x) => constructHoughAccumulator(accum, ...x))
+  const angle = findMaxInHough(accum, points.length - 1)
+
+  if (angle !== undefined) {
+    return {
+      shape: Shapes.line,
+      points,
+    }
   }
+
+  return {}
 }
 
 export const Shapes = {
-- 
GitLab