Skip to content

Commit 5a82e71

Browse files
authored
Merge pull request #170 from nut-tree/enhancement/169/improve-large-img-error-message
Enhancement/169/improve large img error message
2 parents bc1f191 + e779c9b commit 5a82e71

File tree

5 files changed

+90
-79
lines changed

5 files changed

+90
-79
lines changed

lib/match-result.class.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ export class MatchResult {
44
constructor(
55
public readonly confidence: number,
66
public readonly location: Region,
7+
public readonly error?: Error
78
) {}
89
}
41.8 KB
Loading

lib/provider/opencv/template-matching-finder.class.spec.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,7 @@ describe("Template-matching finder", () => {
6565
// THEN
6666
await expect(SUT.findMatch(matchRequest))
6767
.rejects
68-
.toEqual(
69-
expect
70-
.stringMatching(expectedRejection)
71-
);
68+
.toThrowError(expectedRejection);
7269
});
7370

7471
it("findMatch should throw on invalid image paths", async () => {
@@ -91,4 +88,23 @@ describe("Template-matching finder", () => {
9188
.rejects
9289
.toThrowError(`Failed to load ${pathToHaystack}. Reason: 'Failed to load image from '${pathToHaystack}''.`);
9390
});
91+
92+
it("findMatch should reject, if needle was way lager than the haystack", async () => {
93+
// GIVEN
94+
const imageLoader = new ImageReader();
95+
const SUT = new TemplateMatchingFinder();
96+
const haystackPath = path.resolve(__dirname, "./__mocks__/mouse.png");
97+
const needlePath = path.resolve(__dirname, "./__mocks__/fat-needle.png");
98+
const haystack = await imageLoader.load(haystackPath);
99+
const minConfidence = 0.99;
100+
const searchRegion = new Region(0, 0, haystack.width, haystack.height);
101+
const matchRequest = new MatchRequest(haystack, needlePath, searchRegion, minConfidence);
102+
const expectedRejection = new Error("The provided image sample is larger than the provided search region")
103+
104+
// WHEN
105+
const findMatchPromise = SUT.findMatch(matchRequest);
106+
107+
// THEN
108+
await expect(findMatchPromise).rejects.toEqual(expectedRejection)
109+
});
94110
});

lib/provider/opencv/template-matching-finder.class.ts

Lines changed: 67 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ function isValidSearch(needle: cv.Mat, haystack: cv.Mat): boolean {
6060
return (needle.cols <= haystack.cols) && (needle.rows <= haystack.rows);
6161
}
6262

63+
function createResultForInvalidSearch (currentScale: number) {
64+
return new ScaledMatchResult(0,
65+
currentScale,
66+
new Region(
67+
0,
68+
0,
69+
0,
70+
0
71+
),
72+
new Error("The provided image sample is larger than the provided search region")
73+
)
74+
}
75+
6376
export default class TemplateMatchingFinder implements FinderInterface {
6477
private initialScale = [1.0];
6578
private scaleSteps = [0.9, 0.8, 0.7, 0.6, 0.5];
@@ -94,69 +107,15 @@ export default class TemplateMatchingFinder implements FinderInterface {
94107
const matchResults = this.initialScale.map(
95108
async (currentScale) => {
96109
if (!isValidSearch(needle, haystack)) {
97-
return new ScaledMatchResult(0,
98-
currentScale,
99-
new Region(
100-
0,
101-
0,
102-
0,
103-
0
104-
)
105-
);
110+
return createResultForInvalidSearch(currentScale);
106111
}
107112
const matchResult = await matchImages(haystack, needle);
108113
return new ScaledMatchResult(matchResult.confidence, currentScale, matchResult.location);
109114
}
110115
);
116+
111117
if (matchRequest.searchMultipleScales) {
112-
const scaledNeedleResult = this.scaleSteps.map(
113-
async (currentScale) => {
114-
const scaledNeedle = await scaleImage(needle, currentScale);
115-
if (!isValidSearch(scaledNeedle, haystack)) {
116-
return new ScaledMatchResult(0,
117-
currentScale,
118-
new Region(
119-
0,
120-
0,
121-
0,
122-
0
123-
)
124-
);
125-
}
126-
const matchResult = await matchImages(haystack, scaledNeedle);
127-
return new ScaledMatchResult(
128-
matchResult.confidence,
129-
currentScale,
130-
matchResult.location,
131-
);
132-
}
133-
);
134-
const scaledHaystackResult = this.scaleSteps.map(
135-
async (currentScale) => {
136-
const scaledHaystack = await scaleImage(haystack, currentScale);
137-
if (!isValidSearch(needle, scaledHaystack)) {
138-
return new ScaledMatchResult(0,
139-
currentScale,
140-
new Region(
141-
0,
142-
0,
143-
0,
144-
0
145-
)
146-
);
147-
}
148-
const matchResult = await matchImages(scaledHaystack, needle);
149-
return new ScaledMatchResult(
150-
matchResult.confidence,
151-
currentScale,
152-
scaleLocation(
153-
matchResult.location,
154-
currentScale
155-
)
156-
);
157-
}
158-
);
159-
matchResults.push(...scaledHaystackResult, ...scaledNeedleResult);
118+
matchResults.push(...this.searchMultipleScales(needle, haystack))
160119
}
161120

162121
return Promise.all(matchResults).then(results => {
@@ -173,24 +132,58 @@ export default class TemplateMatchingFinder implements FinderInterface {
173132
}
174133

175134
public async findMatch(matchRequest: MatchRequest, debug: boolean = false): Promise<MatchResult> {
176-
return new Promise<MatchResult>(async (resolve, reject) => {
177-
try {
178-
const matches = await this.findMatches(matchRequest, debug);
179-
const potentialMatches = matches
180-
.filter(match => match.confidence >= matchRequest.confidence);
181-
if (potentialMatches.length === 0) {
182-
matches.sort((a, b) => a.confidence - b.confidence);
183-
const bestMatch = matches.pop();
184-
if (bestMatch) {
185-
reject(`No match with required confidence ${matchRequest.confidence}. Best match: ${bestMatch.confidence} at ${bestMatch.location}`)
186-
} else {
187-
reject(`Unable to locate ${matchRequest.pathToNeedle}, no match!`);
188-
}
135+
136+
const matches = await this.findMatches(matchRequest, debug);
137+
const potentialMatches = matches
138+
.filter(match => match.confidence >= matchRequest.confidence);
139+
if (potentialMatches.length === 0) {
140+
matches.sort((a, b) => a.confidence - b.confidence);
141+
const bestMatch = matches.pop();
142+
if (bestMatch) {
143+
if(bestMatch.error) {
144+
throw bestMatch.error
145+
}else {
146+
throw new Error(`No match with required confidence ${matchRequest.confidence}. Best match: ${bestMatch.confidence} at ${bestMatch.location}`)
189147
}
190-
resolve(potentialMatches[0]);
191-
} catch (e) {
192-
reject(e);
148+
} else {
149+
throw new Error(`Unable to locate ${matchRequest.pathToNeedle}, no match!`);
193150
}
194-
});
151+
}
152+
return potentialMatches[0];
153+
}
154+
155+
private searchMultipleScales(needle: cv.Mat, haystack: cv.Mat) {
156+
const scaledNeedleResult = this.scaleSteps.map(
157+
async (currentScale) => {
158+
const scaledNeedle = await scaleImage(needle, currentScale);
159+
if (!isValidSearch(scaledNeedle, haystack)) {
160+
return createResultForInvalidSearch(currentScale);
161+
}
162+
const matchResult = await matchImages(haystack, scaledNeedle);
163+
return new ScaledMatchResult(
164+
matchResult.confidence,
165+
currentScale,
166+
matchResult.location,
167+
);
168+
}
169+
);
170+
const scaledHaystackResult = this.scaleSteps.map(
171+
async (currentScale) => {
172+
const scaledHaystack = await scaleImage(haystack, currentScale);
173+
if (!isValidSearch(needle, scaledHaystack)) {
174+
return createResultForInvalidSearch(currentScale);
175+
}
176+
const matchResult = await matchImages(scaledHaystack, needle);
177+
return new ScaledMatchResult(
178+
matchResult.confidence,
179+
currentScale,
180+
scaleLocation(
181+
matchResult.location,
182+
currentScale
183+
)
184+
);
185+
}
186+
);
187+
return [...scaledHaystackResult, ...scaledNeedleResult];
195188
}
196189
}

lib/scaled-match-result.class.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ export class ScaledMatchResult extends MatchResult {
55
constructor(public readonly confidence: number,
66
public readonly scale: number,
77
public readonly location: Region,
8+
public readonly error?: Error
89
) {
9-
super(confidence, location);
10+
super(confidence, location, error);
1011
}
1112
}

0 commit comments

Comments
 (0)