edge_det 0.1.0 → 0.1.3

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/src/lib.rs DELETED
@@ -1,327 +0,0 @@
1
- use wasm_bindgen::prelude::*;
2
-
3
- const GAUSS: [f32; 5] = [1.0, 4.0, 6.0, 4.0, 1.0];
4
- const GAUSS_SUM: f32 = 16.0;
5
-
6
- fn grayscale(data: &[u8], w: usize, h: usize) -> Vec<f32> {
7
- let n = w * h;
8
- let mut g = vec![0.0f32; n];
9
- for i in 0..n {
10
- let o = i * 4;
11
- g[i] = 0.299 * data[o] as f32 + 0.587 * data[o + 1] as f32 + 0.114 * data[o + 2] as f32;
12
- }
13
- g
14
- }
15
-
16
- fn blur(img: &[f32], w: usize, h: usize) -> Vec<f32> {
17
- let mut tmp = vec![0.0f32; w * h];
18
- let mut out = vec![0.0f32; w * h];
19
- for y in 0..h {
20
- for x in 0..w {
21
- let mut s = 0.0f32;
22
- for k in 0usize..5 {
23
- let xx = ((x as i32) + k as i32 - 2).max(0).min(w as i32 - 1) as usize;
24
- s += img[y * w + xx] * GAUSS[k];
25
- }
26
- tmp[y * w + x] = s / GAUSS_SUM;
27
- }
28
- }
29
- for y in 0..h {
30
- for x in 0..w {
31
- let mut s = 0.0f32;
32
- for k in 0usize..5 {
33
- let yy = ((y as i32) + k as i32 - 2).max(0).min(h as i32 - 1) as usize;
34
- s += tmp[yy * w + x] * GAUSS[k];
35
- }
36
- out[y * w + x] = s / GAUSS_SUM;
37
- }
38
- }
39
- out
40
- }
41
-
42
- struct SobelResult {
43
- mag: Vec<f32>,
44
- dx: Vec<f32>,
45
- dy: Vec<f32>,
46
- }
47
-
48
- fn sobel(img: &[f32], w: usize, h: usize) -> SobelResult {
49
- let n = w * h;
50
- let mut mag = vec![0.0f32; n];
51
- let mut dx = vec![0.0f32; n];
52
- let mut dy = vec![0.0f32; n];
53
- for y in 1..h - 1 {
54
- for x in 1..w - 1 {
55
- let i = y * w + x;
56
- let sx = -img[i - w - 1] - 2.0 * img[i - 1] - img[i + w - 1]
57
- + img[i - w + 1] + 2.0 * img[i + 1] + img[i + w + 1];
58
- let sy = -img[i - w - 1] - 2.0 * img[i - w] - img[i - w + 1]
59
- + img[i + w - 1] + 2.0 * img[i + w] + img[i + w + 1];
60
- dx[i] = sx;
61
- dy[i] = sy;
62
- mag[i] = (sx * sx + sy * sy).sqrt();
63
- }
64
- }
65
- SobelResult { mag, dx, dy }
66
- }
67
-
68
- fn nms(result: &SobelResult, w: usize, h: usize) -> Vec<f32> {
69
- let mut out = vec![0.0f32; w * h];
70
- for y in 1..h - 1 {
71
- for x in 1..w - 1 {
72
- let i = y * w + x;
73
- let m = result.mag[i];
74
- if m < 0.5 {
75
- continue;
76
- }
77
- let gx = result.dx[i];
78
- let gy = result.dy[i];
79
- let abs_gx = gx.abs();
80
- let abs_gy = gy.abs();
81
-
82
- let (mut mag1, mut mag2) = (0.0f32, 0.0f32);
83
- if abs_gx > abs_gy {
84
- let t = abs_gy / abs_gx;
85
- if gx * gy > 0.0 {
86
- mag1 = result.mag[i - w - 1] * (1.0 - t) + result.mag[i - w] * t;
87
- mag2 = result.mag[i + w + 1] * (1.0 - t) + result.mag[i + w] * t;
88
- } else {
89
- mag1 = result.mag[i - w + 1] * (1.0 - t) + result.mag[i - w] * t;
90
- mag2 = result.mag[i + w - 1] * (1.0 - t) + result.mag[i + w] * t;
91
- }
92
- } else if abs_gy > 0.0 {
93
- let t = abs_gx / abs_gy;
94
- if gx * gy > 0.0 {
95
- mag1 = result.mag[i - w - 1] * (1.0 - t) + result.mag[i - 1] * t;
96
- mag2 = result.mag[i + w + 1] * (1.0 - t) + result.mag[i + 1] * t;
97
- } else {
98
- mag1 = result.mag[i - w + 1] * (1.0 - t) + result.mag[i + 1] * t;
99
- mag2 = result.mag[i + w - 1] * (1.0 - t) + result.mag[i - 1] * t;
100
- }
101
- }
102
- out[i] = if m >= mag1 && m >= mag2 { m } else { 0.0 };
103
- }
104
- }
105
- out
106
- }
107
-
108
- fn hysteresis(edges: &mut [u8], w: usize, h: usize) {
109
- let mut stack: Vec<(i32, i32)> = Vec::new();
110
- for y in 0..h {
111
- for x in 0..w {
112
- if edges[y * w + x] == 2 {
113
- stack.push((x as i32, y as i32));
114
- }
115
- }
116
- }
117
- while let Some((x, y)) = stack.pop() {
118
- for dy in -1..=1 {
119
- for dx in -1..=1 {
120
- let nx = x + dx;
121
- let ny = y + dy;
122
- if nx >= 0 && nx < w as i32 && ny >= 0 && ny < h as i32 {
123
- let ni = (ny * w as i32 + nx) as usize;
124
- if edges[ni] == 1 {
125
- edges[ni] = 2;
126
- stack.push((nx, ny));
127
- }
128
- }
129
- }
130
- }
131
- }
132
- for e in edges.iter_mut() {
133
- if *e == 1 {
134
- *e = 0;
135
- }
136
- }
137
- }
138
-
139
- struct UF {
140
- p: Vec<usize>,
141
- r: Vec<u8>,
142
- }
143
-
144
- impl UF {
145
- fn new(n: usize) -> Self {
146
- Self {
147
- p: (0..n).collect(),
148
- r: vec![0; n],
149
- }
150
- }
151
- fn find(&mut self, x: usize) -> usize {
152
- if self.p[x] != x {
153
- self.p[x] = self.find(self.p[x]);
154
- }
155
- self.p[x]
156
- }
157
- fn union(&mut self, a: usize, b: usize) {
158
- let ra = self.find(a);
159
- let rb = self.find(b);
160
- if ra == rb {
161
- return;
162
- }
163
- if self.r[ra] < self.r[rb] {
164
- self.p[ra] = rb;
165
- } else if self.r[ra] > self.r[rb] {
166
- self.p[rb] = ra;
167
- } else {
168
- self.p[rb] = ra;
169
- self.r[ra] += 1;
170
- }
171
- }
172
- }
173
-
174
- fn bounding_boxes(edges: &[u8], w: usize, h: usize, min_area: usize) -> Vec<[i32; 4]> {
175
- let n = w * h;
176
- let mut uf = UF::new(n);
177
- for y in 0..h {
178
- for x in 0..w {
179
- let i = y * w + x;
180
- if edges[i] == 0 {
181
- continue;
182
- }
183
- if x + 1 < w && edges[i + 1] != 0 {
184
- uf.union(i, i + 1);
185
- }
186
- if y + 1 < h && edges[i + w] != 0 {
187
- uf.union(i, i + w);
188
- }
189
- if x + 1 < w && y + 1 < h && edges[i + w + 1] != 0 {
190
- uf.union(i, i + w + 1);
191
- }
192
- if x > 0 && y + 1 < h && edges[i + w - 1] != 0 {
193
- uf.union(i, i + w - 1);
194
- }
195
- }
196
- }
197
- let mut x0 = vec![w as i32; n];
198
- let mut y0 = vec![h as i32; n];
199
- let mut x1 = vec![0i32; n];
200
- let mut y1 = vec![0i32; n];
201
- let mut cnt = vec![0usize; n];
202
- for y in 0..h {
203
- for x in 0..w {
204
- let i = y * w + x;
205
- if edges[i] == 0 {
206
- continue;
207
- }
208
- let r = uf.find(i);
209
- x0[r] = x0[r].min(x as i32);
210
- y0[r] = y0[r].min(y as i32);
211
- x1[r] = x1[r].max(x as i32);
212
- y1[r] = y1[r].max(y as i32);
213
- cnt[r] += 1;
214
- }
215
- }
216
- let mut boxes: Vec<[i32; 4]> = Vec::new();
217
- for i in 0..n {
218
- if cnt[i] >= min_area {
219
- boxes.push([x0[i], y0[i], x1[i] - x0[i] + 1, y1[i] - y0[i] + 1]);
220
- }
221
- }
222
- boxes.sort_by(|a, b| (b[2] * b[3]).cmp(&(a[2] * a[3])));
223
- boxes
224
- }
225
-
226
- fn color_gradient(data: &[u8], w: usize, h: usize) -> SobelResult {
227
- let n = w * h;
228
- let mut r_ch = vec![0.0f32; n];
229
- let mut g_ch = vec![0.0f32; n];
230
- let mut b_ch = vec![0.0f32; n];
231
- for i in 0..n {
232
- let o = i * 4;
233
- r_ch[i] = data[o] as f32;
234
- g_ch[i] = data[o + 1] as f32;
235
- b_ch[i] = data[o + 2] as f32;
236
- }
237
- let rb = blur(&r_ch, w, h);
238
- let gb = blur(&g_ch, w, h);
239
- let bb = blur(&b_ch, w, h);
240
-
241
- let mut mag = vec![0.0f32; n];
242
- let mut dx = vec![0.0f32; n];
243
- let mut dy = vec![0.0f32; n];
244
- for y in 1..h - 1 {
245
- for x in 1..w - 1 {
246
- let i = y * w + x;
247
- let dr_x = rb[i + 1] - rb[i - 1];
248
- let dg_x = gb[i + 1] - gb[i - 1];
249
- let db_x = bb[i + 1] - bb[i - 1];
250
- let dr_y = rb[i + w] - rb[i - w];
251
- let dg_y = gb[i + w] - gb[i - w];
252
- let db_y = bb[i + w] - bb[i - w];
253
- let sx = (dr_x * dr_x + dg_x * dg_x + db_x * db_x).sqrt();
254
- let sy = (dr_y * dr_y + dg_y * dg_y + db_y * db_y).sqrt();
255
- let sign_x = if dr_x + dg_x + db_x >= 0.0 { 1.0 } else { -1.0 };
256
- let sign_y = if dr_y + dg_y + db_y >= 0.0 { 1.0 } else { -1.0 };
257
- dx[i] = sx * sign_x;
258
- dy[i] = sy * sign_y;
259
- mag[i] = (sx * sx + sy * sy).sqrt();
260
- }
261
- }
262
- SobelResult { mag, dx, dy }
263
- }
264
-
265
- fn merge_and_threshold(
266
- gray_sup: &[f32],
267
- color_sup: &[f32],
268
- w: usize,
269
- h: usize,
270
- lo: f32,
271
- hi: f32,
272
- ) -> Vec<u8> {
273
- let n = w * h;
274
- let mut out = vec![0u8; n];
275
- for i in 0..n {
276
- let v = gray_sup[i].max(color_sup[i]);
277
- if v >= hi {
278
- out[i] = 2;
279
- } else if v >= lo {
280
- out[i] = 1;
281
- }
282
- }
283
- out
284
- }
285
-
286
- #[wasm_bindgen]
287
- pub fn detect_borders(
288
- data: &[u8],
289
- width: usize,
290
- height: usize,
291
- low_threshold: f32,
292
- high_threshold: f32,
293
- min_area: usize,
294
- ) -> Vec<f32> {
295
- let gray = grayscale(data, width, height);
296
- let blurred = blur(&gray, width, height);
297
- let gray_sobel = sobel(&blurred, width, height);
298
- let gray_sup = nms(&gray_sobel, width, height);
299
-
300
- let color_sobel = color_gradient(data, width, height);
301
- let color_sup = nms(&color_sobel, width, height);
302
-
303
- let mut edges = merge_and_threshold(
304
- &gray_sup,
305
- &color_sup,
306
- width,
307
- height,
308
- low_threshold,
309
- high_threshold,
310
- );
311
- hysteresis(&mut edges, width, height);
312
-
313
- let boxes = bounding_boxes(&edges, width, height, min_area);
314
- let mut out = Vec::with_capacity(boxes.len() * 4);
315
- for b in &boxes {
316
- out.push(b[0] as f32);
317
- out.push(b[1] as f32);
318
- out.push(b[2] as f32);
319
- out.push(b[3] as f32);
320
- }
321
- out
322
- }
323
-
324
- #[wasm_bindgen]
325
- pub fn detect_borders_default(data: &[u8], width: usize, height: usize) -> Vec<f32> {
326
- detect_borders(data, width, height, 20.0, 60.0, 100)
327
- }
package/src_ts/index.ts DELETED
@@ -1,68 +0,0 @@
1
- import {
2
- initSync,
3
- detect_borders,
4
- detect_borders_default,
5
- } from '../pkg/edge_det.js'
6
- import { WASM_BYTES } from './wasm_bytes'
7
-
8
- export interface Border {
9
- x: number
10
- y: number
11
- w: number
12
- h: number
13
- }
14
-
15
- let _initialized = false
16
-
17
- function ensureInit() {
18
- if (!_initialized) {
19
- initSync({ module: WASM_BYTES })
20
- _initialized = true
21
- }
22
- }
23
-
24
- export function detectBorders(
25
- data: Uint8Array,
26
- width: number,
27
- height: number,
28
- options?: { lowThreshold?: number; highThreshold?: number; minArea?: number }
29
- ): Border[] {
30
- ensureInit()
31
- const result = detect_borders(
32
- data,
33
- width,
34
- height,
35
- options?.lowThreshold ?? 20,
36
- options?.highThreshold ?? 60,
37
- options?.minArea ?? 100
38
- )
39
- const borders: Border[] = []
40
- for (let i = 0; i < result.length; i += 4) {
41
- borders.push({
42
- x: result[i],
43
- y: result[i + 1],
44
- w: result[i + 2],
45
- h: result[i + 3],
46
- })
47
- }
48
- return borders
49
- }
50
-
51
- export function detectBordersDefault(
52
- data: Uint8Array,
53
- width: number,
54
- height: number
55
- ): Border[] {
56
- ensureInit()
57
- const result = detect_borders_default(data, width, height)
58
- const borders: Border[] = []
59
- for (let i = 0; i < result.length; i += 4) {
60
- borders.push({
61
- x: result[i],
62
- y: result[i + 1],
63
- w: result[i + 2],
64
- h: result[i + 3],
65
- })
66
- }
67
- return borders
68
- }
@@ -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
@@ -1,10 +0,0 @@
1
- import { defineConfig } from 'vite'
2
-
3
- export default defineConfig({
4
- build: {
5
- lib: {
6
- entry: 'src_ts/index.ts',
7
- formats: ['es'],
8
- },
9
- },
10
- })
package/vitest.config.ts DELETED
@@ -1,7 +0,0 @@
1
- import { defineConfig } from 'vitest/config'
2
-
3
- export default defineConfig({
4
- test: {
5
- include: ['tests/**/*.test.ts'],
6
- },
7
- })