edge_det 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +100 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +37 -0
- package/dist/wasm_bytes.d.ts +1 -0
- package/dist/wasm_bytes.js +1 -0
- package/package.json +11 -1
- package/.github/workflows/build.yml +0 -36
- package/Cargo.lock +0 -114
- package/Cargo.toml +0 -19
- package/edge_det-0.1.0.tgz +0 -0
- package/pnpm-workspace.yaml +0 -2
- package/scripts/build-wasm.mjs +0 -19
- package/src/lib.rs +0 -327
- package/src_ts/index.ts +0 -68
- package/tests/detection.test.ts +0 -238
- package/tsconfig.json +0 -14
- package/vite.config.ts +0 -10
- package/vitest.config.ts +0 -7
package/tests/detection.test.ts
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import * as PImage from 'pureimage'
|
|
3
|
-
import { detectBorders, detectBordersDefault, Border } from '../src_ts/index'
|
|
4
|
-
|
|
5
|
-
function createImage(
|
|
6
|
-
w: number,
|
|
7
|
-
h: number,
|
|
8
|
-
draw: (ctx: PImage.Context) => void
|
|
9
|
-
): Uint8Array {
|
|
10
|
-
const img = PImage.make(w, h, {})
|
|
11
|
-
const ctx = img.getContext('2d')
|
|
12
|
-
ctx.fillStyle = '#ffffff'
|
|
13
|
-
ctx.fillRect(0, 0, w, h)
|
|
14
|
-
draw(ctx)
|
|
15
|
-
const buf = img.data
|
|
16
|
-
const rgba = new Uint8Array(w * h * 4)
|
|
17
|
-
for (let i = 0; i < w * h; i++) {
|
|
18
|
-
rgba[i * 4] = buf[i * 4]
|
|
19
|
-
rgba[i * 4 + 1] = buf[i * 4 + 1]
|
|
20
|
-
rgba[i * 4 + 2] = buf[i * 4 + 2]
|
|
21
|
-
rgba[i * 4 + 3] = buf[i * 4 + 3]
|
|
22
|
-
}
|
|
23
|
-
return rgba
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function drawRect(
|
|
27
|
-
ctx: PImage.Context,
|
|
28
|
-
x: number,
|
|
29
|
-
y: number,
|
|
30
|
-
w: number,
|
|
31
|
-
h: number,
|
|
32
|
-
color: string,
|
|
33
|
-
lineWidth?: number
|
|
34
|
-
) {
|
|
35
|
-
ctx.strokeStyle = color
|
|
36
|
-
ctx.lineWidth = lineWidth ?? 1
|
|
37
|
-
ctx.strokeRect(x, y, w, h)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function fillRect(
|
|
41
|
-
ctx: PImage.Context,
|
|
42
|
-
x: number,
|
|
43
|
-
y: number,
|
|
44
|
-
w: number,
|
|
45
|
-
h: number,
|
|
46
|
-
color: string
|
|
47
|
-
) {
|
|
48
|
-
ctx.fillStyle = color
|
|
49
|
-
ctx.fillRect(x, y, w, h)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function mergeBounds(borders: Border[]) {
|
|
53
|
-
const m = { x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity }
|
|
54
|
-
for (const b of borders) {
|
|
55
|
-
m.x = Math.min(m.x, b.x)
|
|
56
|
-
m.y = Math.min(m.y, b.y)
|
|
57
|
-
m.x2 = Math.max(m.x2, b.x + b.w)
|
|
58
|
-
m.y2 = Math.max(m.y2, b.y + b.h)
|
|
59
|
-
}
|
|
60
|
-
return m
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function areaOf(b: Border) {
|
|
64
|
-
return b.w * b.h
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function coverage(borders: Border[], expected: { x: number; y: number; w: number; h: number }) {
|
|
68
|
-
const merged = mergeBounds(borders)
|
|
69
|
-
const ex = expected.x
|
|
70
|
-
const ey = expected.y
|
|
71
|
-
const ex2 = expected.x + expected.w
|
|
72
|
-
const ey2 = expected.y + expected.h
|
|
73
|
-
const iw = Math.max(0, Math.min(merged.x2, ex2) - Math.max(merged.x, ex))
|
|
74
|
-
const ih = Math.max(0, Math.min(merged.y2, ey2) - Math.max(merged.y, ey))
|
|
75
|
-
const inter = iw * ih
|
|
76
|
-
const unionArea = (merged.x2 - merged.x) * (merged.y2 - merged.y) + expected.w * expected.h - inter
|
|
77
|
-
return inter / unionArea
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const TOL = 6
|
|
81
|
-
|
|
82
|
-
describe('detectBorders', () => {
|
|
83
|
-
it('solid color → no borders', () => {
|
|
84
|
-
const w = 80, h = 80
|
|
85
|
-
const data = createImage(w, h, () => {})
|
|
86
|
-
const borders = detectBorders(data, w, h)
|
|
87
|
-
expect(borders.length).toBe(0)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('white bg + gray filled rect (low contrast)', () => {
|
|
91
|
-
const w = 200, h = 200
|
|
92
|
-
const rect = { x: 30, y: 40, w: 80, h: 60 }
|
|
93
|
-
const data = createImage(w, h, (ctx) => {
|
|
94
|
-
fillRect(ctx, rect.x, rect.y, rect.w, rect.h, '#cccccc')
|
|
95
|
-
})
|
|
96
|
-
const borders = detectBorders(data, w, h, {
|
|
97
|
-
lowThreshold: 8,
|
|
98
|
-
highThreshold: 25,
|
|
99
|
-
minArea: 20,
|
|
100
|
-
})
|
|
101
|
-
expect(borders.length).toBeGreaterThanOrEqual(1)
|
|
102
|
-
const merged = mergeBounds(borders)
|
|
103
|
-
expect(merged.x).toBeLessThanOrEqual(rect.x + TOL)
|
|
104
|
-
expect(merged.y).toBeLessThanOrEqual(rect.y + TOL)
|
|
105
|
-
expect(merged.x2).toBeGreaterThanOrEqual(rect.x + rect.w - TOL)
|
|
106
|
-
expect(merged.y2).toBeGreaterThanOrEqual(rect.y + rect.h - TOL)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it('white bg + thin black 1px line rect', () => {
|
|
110
|
-
const w = 200, h = 200
|
|
111
|
-
const rect = { x: 20, y: 20, w: 100, h: 80 }
|
|
112
|
-
const data = createImage(w, h, (ctx) => {
|
|
113
|
-
drawRect(ctx, rect.x, rect.y, rect.w, rect.h, '#000000', 1)
|
|
114
|
-
})
|
|
115
|
-
const borders = detectBorders(data, w, h, {
|
|
116
|
-
lowThreshold: 15,
|
|
117
|
-
highThreshold: 45,
|
|
118
|
-
minArea: 10,
|
|
119
|
-
})
|
|
120
|
-
expect(borders.length).toBeGreaterThanOrEqual(1)
|
|
121
|
-
const merged = mergeBounds(borders)
|
|
122
|
-
expect(merged.x).toBeLessThanOrEqual(rect.x + TOL)
|
|
123
|
-
expect(merged.y).toBeLessThanOrEqual(rect.y + TOL)
|
|
124
|
-
expect(merged.x2).toBeGreaterThanOrEqual(rect.x + rect.w - TOL)
|
|
125
|
-
expect(merged.y2).toBeGreaterThanOrEqual(rect.y + rect.h - TOL)
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('white bg + thin black 2px line rect', () => {
|
|
129
|
-
const w = 200, h = 200
|
|
130
|
-
const rect = { x: 50, y: 30, w: 100, h: 120 }
|
|
131
|
-
const data = createImage(w, h, (ctx) => {
|
|
132
|
-
drawRect(ctx, rect.x, rect.y, rect.w, rect.h, '#000000', 2)
|
|
133
|
-
})
|
|
134
|
-
const borders = detectBorders(data, w, h, {
|
|
135
|
-
lowThreshold: 15,
|
|
136
|
-
highThreshold: 45,
|
|
137
|
-
minArea: 10,
|
|
138
|
-
})
|
|
139
|
-
expect(borders.length).toBeGreaterThanOrEqual(1)
|
|
140
|
-
const merged = mergeBounds(borders)
|
|
141
|
-
expect(merged.x).toBeLessThanOrEqual(rect.x + TOL)
|
|
142
|
-
expect(merged.y).toBeLessThanOrEqual(rect.y + TOL)
|
|
143
|
-
expect(merged.x2).toBeGreaterThanOrEqual(rect.x + rect.w - TOL)
|
|
144
|
-
expect(merged.y2).toBeGreaterThanOrEqual(rect.y + rect.h - TOL)
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('white bg + gray thin line rect (low contrast + thin)', () => {
|
|
148
|
-
const w = 200, h = 200
|
|
149
|
-
const rect = { x: 25, y: 25, w: 100, h: 100 }
|
|
150
|
-
const data = createImage(w, h, (ctx) => {
|
|
151
|
-
drawRect(ctx, rect.x, rect.y, rect.w, rect.h, '#999999', 1)
|
|
152
|
-
})
|
|
153
|
-
const borders = detectBorders(data, w, h, {
|
|
154
|
-
lowThreshold: 5,
|
|
155
|
-
highThreshold: 18,
|
|
156
|
-
minArea: 10,
|
|
157
|
-
})
|
|
158
|
-
expect(borders.length).toBeGreaterThanOrEqual(1)
|
|
159
|
-
const merged = mergeBounds(borders)
|
|
160
|
-
expect(merged.x).toBeLessThanOrEqual(rect.x + TOL)
|
|
161
|
-
expect(merged.y).toBeLessThanOrEqual(rect.y + TOL)
|
|
162
|
-
expect(merged.x2).toBeGreaterThanOrEqual(rect.x + rect.w - TOL)
|
|
163
|
-
expect(merged.y2).toBeGreaterThanOrEqual(rect.y + rect.h - TOL)
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
it('blue rect on gray bg (color)', () => {
|
|
167
|
-
const w = 200, h = 200
|
|
168
|
-
const rect = { x: 40, y: 30, w: 80, h: 60 }
|
|
169
|
-
const data = createImage(w, h, (ctx) => {
|
|
170
|
-
fillRect(ctx, 0, 0, w, h, '#555555')
|
|
171
|
-
fillRect(ctx, rect.x, rect.y, rect.w, rect.h, '#0088ff')
|
|
172
|
-
})
|
|
173
|
-
const borders = detectBorders(data, w, h, {
|
|
174
|
-
lowThreshold: 12,
|
|
175
|
-
highThreshold: 35,
|
|
176
|
-
minArea: 20,
|
|
177
|
-
})
|
|
178
|
-
expect(borders.length).toBeGreaterThanOrEqual(1)
|
|
179
|
-
const merged = mergeBounds(borders)
|
|
180
|
-
expect(merged.x).toBeLessThanOrEqual(rect.x + TOL)
|
|
181
|
-
expect(merged.y).toBeLessThanOrEqual(rect.y + TOL)
|
|
182
|
-
expect(merged.x2).toBeGreaterThanOrEqual(rect.x + rect.w - TOL)
|
|
183
|
-
expect(merged.y2).toBeGreaterThanOrEqual(rect.y + rect.h - TOL)
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
it('two rects — count and area', () => {
|
|
187
|
-
const w = 300, h = 300
|
|
188
|
-
const r1 = { x: 20, y: 20, w: 60, h: 60 }
|
|
189
|
-
const r2 = { x: 150, y: 120, w: 100, h: 80 }
|
|
190
|
-
const data = createImage(w, h, (ctx) => {
|
|
191
|
-
fillRect(ctx, r1.x, r1.y, r1.w, r1.h, '#cccccc')
|
|
192
|
-
fillRect(ctx, r2.x, r2.y, r2.w, r2.h, '#cccccc')
|
|
193
|
-
})
|
|
194
|
-
const borders = detectBorders(data, w, h, {
|
|
195
|
-
lowThreshold: 8,
|
|
196
|
-
highThreshold: 25,
|
|
197
|
-
minArea: 20,
|
|
198
|
-
})
|
|
199
|
-
expect(borders.length).toBe(2)
|
|
200
|
-
const areas = borders.map(areaOf).sort((a, b) => a - b)
|
|
201
|
-
const expectedAreas = [r1.w * r1.h, r2.w * r2.h].sort((a, b) => a - b)
|
|
202
|
-
for (let i = 0; i < 2; i++) {
|
|
203
|
-
expect(areas[i]).toBeGreaterThan(expectedAreas[i] * 0.5)
|
|
204
|
-
expect(areas[i]).toBeLessThan(expectedAreas[i] * 2.5)
|
|
205
|
-
}
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
it('two thin-line rects — count and area', () => {
|
|
209
|
-
const w = 300, h = 300
|
|
210
|
-
const r1 = { x: 20, y: 20, w: 80, h: 60 }
|
|
211
|
-
const r2 = { x: 160, y: 100, w: 100, h: 80 }
|
|
212
|
-
const data = createImage(w, h, (ctx) => {
|
|
213
|
-
drawRect(ctx, r1.x, r1.y, r1.w, r1.h, '#000000', 2)
|
|
214
|
-
drawRect(ctx, r2.x, r2.y, r2.w, r2.h, '#000000', 2)
|
|
215
|
-
})
|
|
216
|
-
const borders = detectBorders(data, w, h, {
|
|
217
|
-
lowThreshold: 15,
|
|
218
|
-
highThreshold: 45,
|
|
219
|
-
minArea: 10,
|
|
220
|
-
})
|
|
221
|
-
expect(borders.length).toBeGreaterThanOrEqual(2)
|
|
222
|
-
const sorted = [...borders].sort((a, b) => areaOf(a) - areaOf(b))
|
|
223
|
-
const r1Area = r1.w * r1.h
|
|
224
|
-
const r2Area = r2.w * r2.h
|
|
225
|
-
expect(areaOf(sorted[sorted.length - 1])).toBeGreaterThan(r2Area * 0.3)
|
|
226
|
-
expect(areaOf(sorted[sorted.length - 1])).toBeLessThan(r2Area * 3)
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
it('detectBordersDefault works', () => {
|
|
230
|
-
const w = 200, h = 200
|
|
231
|
-
const rect = { x: 30, y: 30, w: 100, h: 100 }
|
|
232
|
-
const data = createImage(w, h, (ctx) => {
|
|
233
|
-
fillRect(ctx, rect.x, rect.y, rect.w, rect.h, '#cccccc')
|
|
234
|
-
})
|
|
235
|
-
const borders = detectBordersDefault(data, w, h)
|
|
236
|
-
expect(borders.length).toBeGreaterThanOrEqual(1)
|
|
237
|
-
})
|
|
238
|
-
})
|
package/tsconfig.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"declaration": true,
|
|
7
|
-
"outDir": "dist",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true
|
|
11
|
-
},
|
|
12
|
-
"include": ["src_ts"],
|
|
13
|
-
"exclude": ["tests", "node_modules"]
|
|
14
|
-
}
|
package/vite.config.ts
DELETED