From 40f0f158da4eb1720767f8101cd8a7e99be1ef10 Mon Sep 17 00:00:00 2001 From: Yuriy Maksymets <iurii.maksymets@gmail.com> Date: Tue, 12 Nov 2019 22:41:00 +0000 Subject: [PATCH] Matrix coefficient base for rect detection --- __tests__/shape.test.js | 17 ++++++- src/shapes.js | 103 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/__tests__/shape.test.js b/__tests__/shape.test.js index 917d617..9ce3fa2 100644 --- a/__tests__/shape.test.js +++ b/__tests__/shape.test.js @@ -1,4 +1,7 @@ -import recognizeFromPoints, { Shapes } from "../src/shapes" +import recognizeFromPoints, { + Shapes, + computeMatrixCoefficients, +} from "../src/shapes" describe("shape recognition", () => { describe("general", () => { @@ -9,6 +12,18 @@ describe("shape recognition", () => { }) }) + describe("matrix coefficients", () => { + test("should compute coefficients correctly", () => { + const points = [[0, 0], [0, 5], [0, 10], [10, 5]] + const coefficients = computeMatrixCoefficients(points) + expect(coefficients).toStrictEqual([ + [1 / 4, 1 / 4, 1 / 4], + [0, 0, 0], + [0, 1 / 4, 0], + ]) + }) + }) + describe("lines", () => { test("should recognize a simple horizontal line", () => { const points = [[0, 0], [100, 0]] diff --git a/src/shapes.js b/src/shapes.js index c5a37bf..af78778 100644 --- a/src/shapes.js +++ b/src/shapes.js @@ -56,7 +56,98 @@ function constructHoughAccumulator(accumulator, x, y) { } } -function recognizeFromPoints(points) { +function boundingCoords(points) { + const xs = points.map((p) => p[0]) + const ys = points.map((p) => p[1]) + return { + maxX: Math.max(...xs), + minX: Math.min(...xs), + maxY: Math.max(...ys), + minY: Math.min(...ys), + } +} + +const MATRIX_SIZE = 3 + +function mArray(min, max) { + const step = (max - min) / MATRIX_SIZE + return Array(MATRIX_SIZE) + .fill(min) + .map((x, i) => x + step * (i + 1)) +} + +function getCluster([x, y], xBounds, yBounds) { + return { + x: xBounds.findIndex((bound) => x <= bound), + y: yBounds.findIndex((bound) => y <= bound), + } +} + +function computeClusters(points, xBounds, yBounds) { + const clusters = Array(MATRIX_SIZE) + .fill(0) + .map(() => + Array(MATRIX_SIZE) + .fill() + .map(() => []), + ) + points.forEach((point) => { + const { x, y } = getCluster(point, xBounds, yBounds) + clusters[x][y].push(point) + }) + return clusters +} + +function clusterCoefficients(clusters, points) { + return clusters.map((rowCluster) => + rowCluster.map((cluster) => cluster.length / points.length), + ) +} + +export function computeMatrixCoefficients(points) { + const { maxX, minX, maxY, minY } = boundingCoords(points) + const xBounds = mArray(minX, maxX) + const yBounds = mArray(minY, maxY) + const clusters = computeClusters(points, xBounds, yBounds) + const coefficients = clusterCoefficients(clusters, points) + return coefficients +} + +function couldBeLine(points) { + return points.length >= 2 +} + +const RECT_THRESHOLD_CENTER = 0.05 +const RECT_THRESHOLD_SIDE_VARIANCE = 0.2 + +function couldBeRect(points) { + if (points.length < 4) return false + const matrixCoefficients = computeMatrixCoefficients(points) + + let [maxC, minC] = [0, 1] + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + if (!(i === j && j === 1)) { + maxC = Math.max(maxC, matrixCoefficients[i][j]) + minC = Math.min(minC, matrixCoefficients[i][j]) + } + } + } + + if ( + matrixCoefficients[1][1] < RECT_THRESHOLD_CENTER && + maxC - minC < RECT_THRESHOLD_SIDE_VARIANCE + ) { + return true + } + return false +} + +function recognizeRect(points) { + return { points } +} + +function recognizeLine(points) { if (!(points && points.length)) return {} const accum = Array(numAngleCells) points.forEach((x) => constructHoughAccumulator(accum, ...x)) @@ -74,6 +165,16 @@ function recognizeFromPoints(points) { return {} } +function recognizeFromPoints(points) { + if (couldBeRect(points)) { + return recognizeRect(points) + } else if (couldBeLine(points)) { + return recognizeLine(points) + } + + return {} +} + export const Shapes = { rectangle: "rect", line: "line", -- GitLab