Skip to content
This repository was archived by the owner on Jul 26, 2025. It is now read-only.

Commit bfade72

Browse files
committed
fix(getOrientedFastKeypoints): orientation was wrong
1 parent 5107e0f commit bfade72

13 files changed

+161
-126
lines changed

src/featureMatching/descriptors/utils/__tests__/getKeypointPatch.test.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ test.each([
2222
expected: 8,
2323
},
2424
])('default options ($message)', (data) => {
25-
const image = testUtils.load(
26-
`featureMatching/polygons/${data.image}.png` as TestImagePath,
27-
);
25+
const image = testUtils
26+
.load(`featureMatching/polygons/${data.image}.png` as TestImagePath)
27+
.invert();
2828

2929
const grey = image.convertColor(ImageColorModel.GREY);
3030

31-
const allKeypoints = getOrientedFastKeypoints(grey, { windowSize: 15 });
31+
const allKeypoints = getOrientedFastKeypoints(grey, {
32+
centroidPatchDiameter: 15,
33+
});
3234
const keypoints = getBestKeypointsInRadius(allKeypoints, 10);
3335

3436
expect(keypoints).toHaveLength(data.expected);
@@ -43,13 +45,15 @@ test.each([
4345
});
4446

4547
test('patch had black pixels on border', () => {
46-
const image = testUtils.load(
47-
`featureMatching/polygons/polygonRotated180degrees.png`,
48-
);
48+
const image = testUtils
49+
.load(`featureMatching/polygons/polygonRotated180degrees.png`)
50+
.invert();
4951

5052
const grey = image.convertColor(ImageColorModel.GREY);
5153

52-
const allKeypoints = getOrientedFastKeypoints(grey, { windowSize: 15 });
54+
const allKeypoints = getOrientedFastKeypoints(grey, {
55+
centroidPatchDiameter: 15,
56+
});
5357
const keypoints = getBestKeypointsInRadius(allKeypoints, 10);
5458
const keypoint = keypoints[4];
5559

@@ -79,9 +83,12 @@ test.each([
7983
])('windowSize = 15 ($message)', (data) => {
8084
const image = testUtils
8185
.load(`featureMatching/polygons/${data.image}.png` as TestImagePath)
82-
.convertColor(ImageColorModel.GREY);
86+
.convertColor(ImageColorModel.GREY)
87+
.invert();
8388

84-
const keypoints = getOrientedFastKeypoints(image, { windowSize: 15 });
89+
const keypoints = getOrientedFastKeypoints(image, {
90+
centroidPatchDiameter: 15,
91+
});
8592

8693
for (let keypoint of keypoints) {
8794
expect(getKeypointPatch(image, keypoint)).toMatchImageSnapshot();
Loading
Loading
Lines changed: 131 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import { TestImagePath } from '../../../../test/TestImagePath';
12
import { ImageColorModel } from '../../../Image';
23
import { drawKeypoints } from '../../visualize/drawKeypoints';
34
import { getOrientedFastKeypoints } from '../getOrientedFastKeypoints';
45

5-
test('7x7 image, angle = 90°', () => {
6+
test('7x7 image, angle = -90°', () => {
67
const image = testUtils.createGreyImage([
78
[0, 0, 0, 0, 0, 0, 0],
89
[0, 0, 0, 0, 0, 0, 0],
@@ -15,7 +16,45 @@ test('7x7 image, angle = 90°', () => {
1516

1617
const result = getOrientedFastKeypoints(image)[0];
1718
expect(result).toStrictEqual({
18-
angle: 90,
19+
angle: -90,
20+
origin: { row: 3, column: 3 },
21+
score: 2780,
22+
});
23+
});
24+
25+
test('7x7 image, angle = 135°', () => {
26+
const image = testUtils.createGreyImage([
27+
[100, 0, 0, 0, 0, 0, 0],
28+
[0, 100, 0, 0, 0, 0, 0],
29+
[0, 0, 100, 0, 0, 0, 0],
30+
[0, 0, 0, 200, 0, 0, 0],
31+
[0, 0, 0, 0, 0, 0, 0],
32+
[0, 0, 0, 0, 0, 0, 0],
33+
[0, 0, 0, 0, 0, 0, 0],
34+
]);
35+
36+
const result = getOrientedFastKeypoints(image)[0];
37+
expect(result).toStrictEqual({
38+
angle: -225,
39+
origin: { row: 3, column: 3 },
40+
score: 2780,
41+
});
42+
});
43+
44+
test('7x7 image, angle = -135°', () => {
45+
const image = testUtils.createGreyImage([
46+
[0, 0, 0, 0, 0, 0, 0],
47+
[0, 0, 0, 0, 0, 0, 0],
48+
[0, 0, 0, 0, 0, 0, 0],
49+
[0, 0, 0, 200, 0, 0, 0],
50+
[0, 0, 100, 0, 0, 0, 0],
51+
[0, 100, 0, 0, 0, 0, 0],
52+
[100, 0, 0, 0, 0, 0, 0],
53+
]);
54+
55+
const result = getOrientedFastKeypoints(image)[0];
56+
expect(result).toStrictEqual({
57+
angle: 225,
1958
origin: { row: 3, column: 3 },
2059
score: 2780,
2160
});
@@ -53,12 +92,12 @@ test('7x7 image, angle = 0°', () => {
5392

5493
const result = getOrientedFastKeypoints(image)[0];
5594
expect(result).toStrictEqual({
56-
angle: 0,
95+
angle: -0,
5796
origin: { row: 3, column: 3 },
5897
score: 2680,
5998
});
6099
});
61-
test('7x7 image, angle = -45°', () => {
100+
test('7x7 image, angle = 45°', () => {
62101
const image = testUtils.createGreyImage([
63102
[0, 0, 0, 0, 0, 0, 100],
64103
[0, 0, 0, 0, 0, 100, 0],
@@ -69,6 +108,25 @@ test('7x7 image, angle = -45°', () => {
69108
[0, 0, 0, 0, 0, 0, 0],
70109
]);
71110

111+
const result = getOrientedFastKeypoints(image)[0];
112+
expect(result).toBeDeepCloseTo({
113+
angle: 45,
114+
origin: { row: 3, column: 3 },
115+
score: 2780,
116+
});
117+
});
118+
119+
test('7x7 image, angle = -45°', () => {
120+
const image = testUtils.createGreyImage([
121+
[0, 0, 0, 0, 0, 0, 0],
122+
[0, 0, 0, 0, 0, 0, 0],
123+
[0, 0, 0, 0, 0, 0, 0],
124+
[0, 0, 0, 200, 0, 0, 0],
125+
[0, 0, 0, 0, 100, 0, 0],
126+
[0, 0, 0, 0, 0, 100, 0],
127+
[0, 0, 0, 0, 0, 0, 100],
128+
]);
129+
72130
const result = getOrientedFastKeypoints(image)[0];
73131
expect(result).toBeDeepCloseTo({
74132
angle: -45,
@@ -77,6 +135,44 @@ test('7x7 image, angle = -45°', () => {
77135
});
78136
});
79137

138+
test('patch with one keypoint', () => {
139+
const image = testUtils.load('featureMatching/patch.png').invert();
140+
141+
const keypoints = getOrientedFastKeypoints(image);
142+
143+
expect(
144+
drawKeypoints(image, keypoints, { markerSize: 7, showOrientation: true }),
145+
).toMatchImageSnapshot();
146+
147+
expect(keypoints).toBeDeepCloseTo([
148+
{
149+
angle: -187.205,
150+
origin: { row: 15, column: 14 },
151+
score: 3290,
152+
},
153+
]);
154+
});
155+
156+
test('patch with one keypoint, centroidPatchDiameter=15', () => {
157+
const image = testUtils.load('featureMatching/patch.png').invert();
158+
159+
const keypoints = getOrientedFastKeypoints(image, {
160+
centroidPatchDiameter: 15,
161+
});
162+
163+
expect(
164+
drawKeypoints(image, keypoints, { markerSize: 7, showOrientation: true }),
165+
).toMatchImageSnapshot();
166+
167+
expect(keypoints).toBeDeepCloseTo([
168+
{
169+
angle: 191.8,
170+
origin: { row: 15, column: 14 },
171+
score: 3290,
172+
},
173+
]);
174+
});
175+
80176
test('check we handle edge cases properly', () => {
81177
const image = testUtils
82178
.load('featureMatching/crop1.png')
@@ -98,111 +194,42 @@ test('angle should never be NaN', () => {
98194
}
99195
});
100196

101-
test('check angle for different windowSize', () => {
102-
const image = testUtils
103-
.load('featureMatching/polygons/scaleneTriangle10.png')
104-
.convertColor(ImageColorModel.GREY);
105-
106-
const keypoints7 = getOrientedFastKeypoints(image);
107-
const keypoints15 = getOrientedFastKeypoints(image, { windowSize: 15 });
108-
const keypoints31 = getOrientedFastKeypoints(image, { windowSize: 31 });
109-
110-
expect([keypoints7, keypoints15, keypoints31]).toBeDeepCloseTo(
111-
[
112-
[
113-
{
114-
origin: { row: 607, column: 132 },
115-
score: 2680,
116-
angle: 145.3,
117-
},
118-
{
119-
origin: { row: 50, column: 292 },
120-
score: 2662,
121-
angle: -112.2,
122-
},
123-
],
124-
[
125-
{
126-
origin: { row: 607, column: 132 },
127-
score: 2680,
128-
angle: 123.7,
129-
},
130-
{
131-
origin: { row: 50, column: 292 },
132-
score: 2662,
133-
angle: -95.4,
134-
},
135-
],
136-
[
137-
{
138-
origin: { row: 607, column: 132 },
139-
score: 2680,
140-
angle: 120,
141-
},
142-
{
143-
origin: { row: 50, column: 292 },
144-
score: 2662,
145-
angle: -92.1,
146-
},
147-
],
148-
],
149-
1,
150-
);
151-
});
152-
153-
test('angle diff should be 90°', () => {
154-
const windowSize = 15;
155-
const markerSize = 15;
197+
test.each([
198+
{
199+
message: 'betterScaleneTriangle',
200+
image: 'betterScaleneTriangle',
201+
},
202+
{
203+
message: 'betterScaleneTriangle90',
204+
image: 'betterScaleneTriangle90',
205+
},
206+
])('orientation should look correct ($message)', (data) => {
207+
const markerSize = 7;
156208

157209
const image = testUtils
158-
.load('featureMatching/polygons/betterScaleneTriangle.png')
159-
.convertColor(ImageColorModel.GREY);
160-
161-
const rotated = testUtils
162-
.load('featureMatching/polygons/betterScaleneTriangle90.png')
163-
.convertColor(ImageColorModel.GREY);
164-
165-
const keypoints = getOrientedFastKeypoints(image, { windowSize });
166-
const keypointsRotated = getOrientedFastKeypoints(rotated, { windowSize });
167-
console.log(keypoints, keypointsRotated);
210+
.load(`featureMatching/polygons/${data.image}.png` as TestImagePath)
211+
.convertColor(ImageColorModel.GREY)
212+
.invert();
213+
const keypoints = getOrientedFastKeypoints(image);
168214

169215
expect(
170216
drawKeypoints(image, keypoints, { markerSize, showOrientation: true }),
171217
).toMatchImageSnapshot();
172-
expect(
173-
drawKeypoints(rotated, keypointsRotated, {
174-
markerSize,
175-
showOrientation: true,
176-
}),
177-
).toMatchImageSnapshot();
218+
});
178219

179-
expect([keypoints, keypointsRotated]).toBeDeepCloseTo(
180-
[
181-
[
182-
{
183-
origin: { row: 607, column: 132 },
184-
score: 2680,
185-
angle: 145.3,
186-
},
187-
{
188-
origin: { row: 50, column: 292 },
189-
score: 2662,
190-
angle: -112.2,
191-
},
192-
],
193-
[
194-
{
195-
origin: { row: 607, column: 132 },
196-
score: 2680,
197-
angle: 123.7,
198-
},
199-
{
200-
origin: { row: 50, column: 292 },
201-
score: 2662,
202-
angle: -95.4,
203-
},
204-
],
205-
],
206-
1,
207-
);
220+
test('check angle for different windowSize', () => {
221+
const image = testUtils
222+
.load('featureMatching/polygons/scaleneTriangle10.png')
223+
.convertColor(ImageColorModel.GREY)
224+
.invert();
225+
226+
const angle77 = getOrientedFastKeypoints(image)[0].angle;
227+
const angle15 = getOrientedFastKeypoints(image, {
228+
centroidPatchDiameter: 15,
229+
})[0].angle;
230+
const angle31 = getOrientedFastKeypoints(image, {
231+
centroidPatchDiameter: 31,
232+
})[0].angle;
233+
234+
expect([angle77, angle15, angle31]).toBeDeepCloseTo([69.81, 62.84, 61.79], 0);
208235
});

src/featureMatching/keypoints/__tests__/getPatchIntensityCentroid.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ test('patch, default options', () => {
6161
const radius = 3;
6262

6363
const centroid = getPatchIntensityCentroid(image)[0];
64+
expect(centroid).toBeDeepCloseTo({ column: -1.179, row: -0.183 });
6465

6566
const center = image.getCoordinates(ImageCoordinates.CENTER);
6667

src/featureMatching/keypoints/getOrientedFastKeypoints.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Image } from '../../Image';
2-
import { getAngle } from '../../maskAnalysis/utils/getAngle';
2+
import { getMathAngle } from '../../maskAnalysis/utils/getAngle';
33
import { toDegrees } from '../../utils/geometry/angles';
44
import { getRadius } from '../../utils/getRadius';
55
import { checkBorderDistance } from '../utils/checkBorderDistance';
@@ -14,16 +14,16 @@ import { getPatchIntensityCentroid } from './getPatchIntensityCentroid';
1414
export interface GetOrientedFastKeypointsOptions
1515
extends GetFastKeypointsOptions {
1616
/**
17-
* Window size for the intensity centroid computation.
17+
* Diameter of the circle used for compotuation of the intensity centroid.
1818
*
1919
* @default 7
2020
*/
21-
windowSize?: number;
21+
centroidPatchDiameter?: number;
2222
}
2323

2424
export interface OrientedFastKeypoint extends FastKeypoint {
2525
/**
26-
* Clockwise angle of the keypoint in degrees with regard to a horizontal line.
26+
* Orientation of the keypoint defined as the angle in degrees between the x axis , the keypoints origin and the center of mass of the keypoint.
2727
*/
2828
angle: number;
2929
}
@@ -42,7 +42,7 @@ export function getOrientedFastKeypoints(
4242
image: Image,
4343
options: GetOrientedFastKeypointsOptions = {},
4444
): OrientedFastKeypoint[] {
45-
const { windowSize = 7 } = options;
45+
const { centroidPatchDiameter: windowSize = 7 } = options;
4646

4747
const fastKeypoints = getFastKeypoints(image, options);
4848

@@ -62,7 +62,7 @@ export function getOrientedFastKeypoints(
6262
center: keypoint.origin,
6363
radius,
6464
})[0];
65-
const angle = toDegrees(getAngle({ column: 0, row: 0 }, centroid));
65+
const angle = toDegrees(getMathAngle({ column: 0, row: 0 }, centroid));
6666
orientedFastKeypoints.push({ ...keypoint, angle });
6767
}
6868
return orientedFastKeypoints;

0 commit comments

Comments
 (0)