Skip to content
Snippets Groups Projects
Commit 0c2de929 authored by Yuriy Maksymets's avatar Yuriy Maksymets
Browse files

Rectangle position and recognition

parent 05429a34
No related branches found
No related tags found
No related merge requests found
......@@ -244,15 +244,15 @@ describe("shape recognition", () => {
})
test("should recognize almost-closed rectangle", () => {
const points = [[-10, -10], [10, -10], [10, 10], [-9, -9]]
const points = [[-10, -10], [10, -10], [10, 10], [-9, 9]]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.rectangle)
})
test("should not recognize half-closed rectangle", () => {
test("should recognize half-closed rectangle", () => {
const points = [[-10, -10], [10, -10], [10, 10], [-1, -9]]
const result = recognizeFromPoints(points)
expect(result.shape).not.toBe(Shapes.rectangle)
expect(result.shape).toBe(Shapes.rectangle)
})
})
})
......@@ -7,7 +7,7 @@ import * as canvas from "./canvas.js"
import * as HTML from "./elements.js"
import { connect } from "./room.js"
import * as toolSelection from "./tool-selection.js"
import recognizeFromPoints from "./shapes.js"
import recognizeFromPoints, { Shapes } from "./shapes.js"
const TEST_ROOM = "imperial"
......@@ -107,15 +107,22 @@ const onRoomConnect = (room_) => {
}
const mp = (x, y) => [x, y, 1, "black", true]
function drawRecognized(points) {
const recognizedShape = recognizeFromPoints(points)
if (recognizedShape.shape) {
if (recognizedShape.shape === Shapes.line) {
console.log(recognizedShape)
const [x, y] = points[0]
const a = (recognizedShape.angle * Math.PI) / 180
const [x0, y0] = [x - 2000 * Math.cos(a), y + 2000 * Math.sin(a)]
const [x1, y1] = [x + 2000 * Math.cos(a), y - 2000 * Math.sin(a)]
canvas.renderPath("lastRecognizedLine", [mp(x0, y0), mp(x1, y1)])
} else if (recognizedShape.shape === Shapes.rectangle) {
console.log(recognizedShape)
canvas.renderPath(
"lastRecognizedLine",
recognizedShape.boundingPoints.map((x) => mp(...x)),
)
} else {
canvas.renderPath("lastRecognizedLine", [])
}
......
......@@ -12,6 +12,13 @@ const angleStep = 10
const numAngleCells = 180 / angleStep
const rhoMax = 1000
const getDistance = (a, b) => {
if (!(a & b)) return 0
return Math.sqrt(
(a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]),
)
}
function findMaxInHough(accum, threshold) {
let max = 0
// let bestRho = 0
......@@ -67,12 +74,14 @@ function boundingCoords(points) {
}
const MATRIX_SIZE = 3
const MATRIX_CENTER_RATIO = 0.65
function mArray(min, max) {
const step = (max - min) / MATRIX_SIZE
return Array(MATRIX_SIZE)
.fill(min)
.map((x, i) => x + step * (i + 1))
const d = max - min
const centerSegmentSize = d * MATRIX_CENTER_RATIO
const smallStep = (d - centerSegmentSize) / 2
const p = [min + smallStep, min + smallStep + centerSegmentSize, max]
return p
}
function getCluster([x, y], xBounds, yBounds) {
......@@ -90,10 +99,16 @@ function computeClusters(points, xBounds, yBounds) {
.fill()
.map(() => []),
)
points.forEach((point) => {
const { x, y } = getCluster(point, xBounds, yBounds)
clusters[x][y].push(point)
const intervals = points.map((point, i) => ({
point,
dist: getDistance(point, points[i + 1]),
}))
intervals.forEach((interval) => {
const { x, y } = getCluster(interval.point, xBounds, yBounds)
clusters[x][y].push(interval)
})
return clusters
}
......@@ -103,8 +118,8 @@ function clusterCoefficients(clusters, points) {
)
}
export function computeMatrixCoefficients(points) {
const { maxX, minX, maxY, minY } = boundingCoords(points)
export function computeMatrixCoefficients(points, boundingRect) {
const { maxX, minX, maxY, minY } = boundingRect
const xBounds = mArray(minX, maxX)
const yBounds = mArray(minY, maxY)
const clusters = computeClusters(points, xBounds, yBounds)
......@@ -112,16 +127,22 @@ export function computeMatrixCoefficients(points) {
return coefficients
}
const LINE_Q = 10
function couldBeLine(points) {
return points.length >= 2
const { maxX, minX, maxY, minY } = boundingCoords(points)
const [dx, dy] = [maxX - minX, maxY - minY].map((x) => x + 0.00001)
return dy / dx > LINE_Q || dx / dy > LINE_Q
}
const RECT_THRESHOLD_CENTER = 0.05
const RECT_THRESHOLD_SIDE_VARIANCE = 0.2
const RECT_THRESHOLD_SIDE_VARIANCE = 0.12
function couldBeRect(points) {
if (points.length < 4) return false
const matrixCoefficients = computeMatrixCoefficients(points)
const boundingRect = boundingCoords(points)
const matrixCoefficients = computeMatrixCoefficients(points, boundingRect)
let [maxC, minC] = [0, 1]
for (let i = 0; i < 3; i++) {
......@@ -133,24 +154,40 @@ function couldBeRect(points) {
}
}
console.log(matrixCoefficients)
if (
matrixCoefficients[1][1] < RECT_THRESHOLD_CENTER &&
maxC - minC < RECT_THRESHOLD_SIDE_VARIANCE
(matrixCoefficients[1][1] < RECT_THRESHOLD_CENTER &&
maxC - minC < RECT_THRESHOLD_SIDE_VARIANCE) ||
(maxC - minC < RECT_THRESHOLD_SIDE_VARIANCE * 2 &&
matrixCoefficients[1][1] === 0)
) {
return true
return { coefficients: matrixCoefficients, boundingRect }
}
return false
return undefined
}
function recognizeRect(points) {
return { points }
function recognizeRect(points, rectDetectionData) {
const { minX, minY, maxX, maxY } = rectDetectionData.boundingRect
return {
boundingRect: rectDetectionData.boundingRect,
boundingPoints: [
[minX, minY],
[minX, maxY],
[maxX, maxY],
[maxX, minY],
[minX, minY],
],
shape: Shapes.rectangle,
points,
}
}
function recognizeLine(points) {
if (!(points && points.length)) return {}
const accum = Array(numAngleCells)
const houghConfig = {
rhoStep: points.length > 100 ? 50 : 5,
rhoStep: points.length > 50 ? 50 : 5,
}
points.forEach((x) => constructHoughAccumulator(houghConfig, accum, ...x))
const angle = findMaxInHough(accum, points.length - 1)
......@@ -168,8 +205,9 @@ function recognizeLine(points) {
}
function recognizeFromPoints(points) {
if (couldBeRect(points)) {
return recognizeRect(points)
const rectDetectData = couldBeRect(points)
if (rectDetectData) {
return recognizeRect(points, rectDetectData)
} else if (couldBeLine(points)) {
return recognizeLine(points)
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment