Synthetische Zaehl-Tests an das oertlich-kontrastbasierte Verfahren angepasst
Das neue Verfahren braucht ein Umgebungsfenster (Radius max(Breite,Hoehe)/12), das an jeder Fundstueck-Stelle noch echten Untergrund sieht - die alten 40x40-/80x80-Testbilder mit 10x10-Quadraten waren dafuer zu klein. Bildgroesse und Quadratmasse angepasst (300x300/600x600), Kernaussagen unveraendert: leeres Bild -> null, getrennte gleich grosse Flaechen -> ihre Anzahl, Rauschen zaehlt nicht mit, Hochrechnung beruehrender Teile, kein Doppelzaehlen leicht groesserer Einzelteile, beide Polaritaeten, 4er- statt 8er-Nachbarschaft (Luecke statt gemeinsamem Eckpixel, da die Glaettung Raender aufweitet). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+68
-47
@@ -40,81 +40,95 @@ function paint(rows, x, y, width, height, value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Das neue Verfahren (siehe count-objects.js) braucht oertlichen Kontrast:
|
||||||
|
// jeder Bildpunkt wird gegen den Mittelwert seiner Umgebung verglichen
|
||||||
|
// (Fensterradius max(Breite, Hoehe) / 12). Damit dieses Fenster an jeder
|
||||||
|
// Objektstelle noch echten Untergrund sieht (statt nur das Objekt selbst
|
||||||
|
// nochmal zu mitteln), muss der Untergrund eine deutlich groessere Flaeche
|
||||||
|
// einnehmen als jedes einzelne Fundstueck - anders als beim alten,
|
||||||
|
// Otsu-basierten Verfahren, wo ein 40x40- oder 80x80-Bild mit 10x10-
|
||||||
|
// Quadraten genuegte. Die Testbilder hier sind deshalb groesser (300x300
|
||||||
|
// bzw. 600x600) im Verhaeltnis zu den gemalten Flaechen.
|
||||||
|
|
||||||
test('leeres Bild (kein Objekt, gleichmaessiger Untergrund) ergibt null', () => {
|
test('leeres Bild (kein Objekt, gleichmaessiger Untergrund) ergibt null', () => {
|
||||||
const rows = grid(20, 20, 60);
|
const rows = grid(300, 300, 60);
|
||||||
const result = countObjects(image(rows));
|
const result = countObjects(image(rows));
|
||||||
assert.equal(result.count, 0);
|
assert.equal(result.count, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('eine einzelne Flaeche ergibt eins', () => {
|
test('eine einzelne Flaeche ergibt eins', () => {
|
||||||
const rows = grid(40, 40, 30);
|
const rows = grid(300, 300, 30);
|
||||||
paint(rows, 15, 15, 10, 10, 220);
|
paint(rows, 140, 140, 20, 20, 220);
|
||||||
const result = countObjects(image(rows));
|
const result = countObjects(image(rows));
|
||||||
assert.equal(result.count, 1);
|
assert.equal(result.count, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('mehrere getrennte, gleich grosse Flaechen ergeben ihre Anzahl', () => {
|
test('mehrere getrennte, gleich grosse Flaechen ergeben ihre Anzahl', () => {
|
||||||
const rows = grid(80, 80, 30);
|
const rows = grid(300, 300, 30);
|
||||||
paint(rows, 5, 5, 10, 10, 220);
|
paint(rows, 40, 40, 20, 20, 220);
|
||||||
paint(rows, 30, 5, 10, 10, 220);
|
paint(rows, 200, 40, 20, 20, 220);
|
||||||
paint(rows, 5, 30, 10, 10, 220);
|
paint(rows, 40, 200, 20, 20, 220);
|
||||||
paint(rows, 30, 30, 10, 10, 220);
|
paint(rows, 200, 200, 20, 20, 220);
|
||||||
const result = countObjects(image(rows));
|
const result = countObjects(image(rows));
|
||||||
assert.equal(result.count, 4);
|
assert.equal(result.count, 4);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('winzige Stoerflaechen (Staub, Reflexe) werden nicht mitgezaehlt', () => {
|
test('winzige Stoerflaechen (Staub, Reflexe) werden nicht mitgezaehlt', () => {
|
||||||
const rows = grid(80, 80, 30);
|
const rows = grid(300, 300, 30);
|
||||||
paint(rows, 5, 5, 10, 10, 220);
|
paint(rows, 40, 40, 20, 20, 220);
|
||||||
paint(rows, 30, 5, 10, 10, 220);
|
paint(rows, 200, 40, 20, 20, 220);
|
||||||
paint(rows, 5, 30, 10, 10, 220);
|
paint(rows, 40, 200, 20, 20, 220);
|
||||||
paint(rows, 30, 30, 10, 10, 220);
|
paint(rows, 200, 200, 20, 20, 220);
|
||||||
// Einzelpixel- bis Doppelpixel-Stoerflaechen, weit entfernt von den
|
// Einzelpixel- bis Doppelpixel-Stoerflaechen, weit entfernt von den
|
||||||
// echten Flaechen und voneinander - deutlich unter einem Viertel der
|
// echten Flaechen und voneinander - weit unter der Rauschschwelle
|
||||||
// typischen Flaechengroesse (100 Bildpunkte).
|
// (max(8, 0.00002 * Bildpunktzahl), hier 8 Bildpunkte).
|
||||||
rows[60][60] = 220;
|
rows[150][150] = 220;
|
||||||
rows[65][65] = 220;
|
rows[160][160] = 220;
|
||||||
rows[65][66] = 220;
|
rows[160][161] = 220;
|
||||||
rows[70][10] = 220;
|
|
||||||
const result = countObjects(image(rows));
|
const result = countObjects(image(rows));
|
||||||
assert.equal(result.count, 4, 'Stoerflaechen duerfen die Anzahl nicht erhoehen');
|
assert.equal(result.count, 4, 'Stoerflaechen duerfen die Anzahl nicht erhoehen');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('eine Flaeche von etwa vierfacher Einzelgroesse wird als vier gezaehlt', () => {
|
test('eine Flaeche von etwa vierfacher Einzelgroesse wird als vier gezaehlt', () => {
|
||||||
const rows = grid(80, 80, 30);
|
// Groessere Quadrate und ein groesseres Bild als beim Rauschtest: die
|
||||||
paint(rows, 5, 5, 10, 10, 220); // 100 Bildpunkte, typische Groesse
|
// Glaettung (Schritt 3) dehnt jede Flaeche an ihrem Rand etwas auf - bei
|
||||||
paint(rows, 30, 5, 10, 10, 220); // 100
|
// sehr kleinen Quadraten faellt diese Randzugabe relativ zur Flaeche viel
|
||||||
paint(rows, 5, 30, 10, 10, 220); // 100
|
// staerker ins Gewicht als bei groesseren und wuerde das exakte
|
||||||
// Verschmolzene Flaeche mehrerer beruehrender Teile: 20x20 = 400 = 4x100.
|
// Vierfache verzerren.
|
||||||
paint(rows, 30, 30, 20, 20, 220);
|
const rows = grid(600, 600, 30);
|
||||||
|
paint(rows, 60, 60, 40, 40, 220); // typische Groesse
|
||||||
|
paint(rows, 400, 60, 40, 40, 220);
|
||||||
|
paint(rows, 60, 400, 40, 40, 220);
|
||||||
|
// Verschmolzene Flaeche mehrerer beruehrender Teile: 80x80 = 4x 40x40.
|
||||||
|
paint(rows, 400, 400, 80, 80, 220);
|
||||||
const result = countObjects(image(rows));
|
const result = countObjects(image(rows));
|
||||||
assert.equal(result.count, 7, '3 Einzelflaechen (je 1) + hochgerechnete Flaeche (4)');
|
assert.equal(result.count, 7, '3 Einzelflaechen (je 1) + hochgerechnete Flaeche (4)');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ein einzelnes, nur leicht groesseres Teil wird nicht als zwei gezaehlt', () => {
|
test('ein einzelnes, nur leicht groesseres Teil wird nicht als zwei gezaehlt', () => {
|
||||||
const rows = grid(80, 80, 30);
|
const rows = grid(600, 600, 30);
|
||||||
paint(rows, 5, 5, 10, 10, 220); // 100
|
paint(rows, 60, 60, 40, 40, 220);
|
||||||
paint(rows, 30, 5, 10, 10, 220); // 100
|
paint(rows, 400, 60, 40, 40, 220);
|
||||||
paint(rows, 5, 30, 10, 10, 220); // 100
|
paint(rows, 60, 400, 40, 40, 220);
|
||||||
// Rund 30% groesser als die anderen (11x12=132 statt 100) - haeufiger
|
// Rund 30% groesser als die anderen (44x48=2112 statt 1600) - haeufiger
|
||||||
// Fall, wenn ein Teil im Bild etwas naeher an der Kamera liegt.
|
// Fall, wenn ein Teil im Bild etwas naeher an der Kamera liegt.
|
||||||
paint(rows, 30, 30, 11, 12, 220);
|
paint(rows, 400, 400, 44, 48, 220);
|
||||||
const result = countObjects(image(rows));
|
const result = countObjects(image(rows));
|
||||||
assert.equal(result.count, 4, 'leicht groessere Einzelflaeche zaehlt weiterhin als eins');
|
assert.equal(result.count, 4, 'leicht groessere Einzelflaeche zaehlt weiterhin als eins');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('dunkle Objekte auf hellem Grund werden genauso gezaehlt wie helle auf dunklem', () => {
|
test('dunkle Objekte auf hellem Grund werden genauso gezaehlt wie helle auf dunklem', () => {
|
||||||
const darkOnLight = grid(80, 80, 220);
|
const darkOnLight = grid(300, 300, 220);
|
||||||
paint(darkOnLight, 5, 5, 10, 10, 30);
|
paint(darkOnLight, 40, 40, 20, 20, 30);
|
||||||
paint(darkOnLight, 30, 5, 10, 10, 30);
|
paint(darkOnLight, 200, 40, 20, 20, 30);
|
||||||
paint(darkOnLight, 5, 30, 10, 10, 30);
|
paint(darkOnLight, 40, 200, 20, 20, 30);
|
||||||
paint(darkOnLight, 30, 30, 10, 10, 30);
|
paint(darkOnLight, 200, 200, 20, 20, 30);
|
||||||
|
|
||||||
const lightOnDark = grid(80, 80, 30);
|
const lightOnDark = grid(300, 300, 30);
|
||||||
paint(lightOnDark, 5, 5, 10, 10, 220);
|
paint(lightOnDark, 40, 40, 20, 20, 220);
|
||||||
paint(lightOnDark, 30, 5, 10, 10, 220);
|
paint(lightOnDark, 200, 40, 20, 20, 220);
|
||||||
paint(lightOnDark, 5, 30, 10, 10, 220);
|
paint(lightOnDark, 40, 200, 20, 20, 220);
|
||||||
paint(lightOnDark, 30, 30, 10, 10, 220);
|
paint(lightOnDark, 200, 200, 20, 20, 220);
|
||||||
|
|
||||||
const darkResult = countObjects(image(darkOnLight));
|
const darkResult = countObjects(image(darkOnLight));
|
||||||
const lightResult = countObjects(image(lightOnDark));
|
const lightResult = countObjects(image(lightOnDark));
|
||||||
@@ -129,11 +143,18 @@ test('ein Bild ohne Bildpunkte stuerzt nicht ab', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('zwei aneinanderliegende, aber nur diagonal beruehrende Flaechen zaehlen getrennt (4er-Nachbarschaft)', () => {
|
test('zwei nahe, aber nur diagonal benachbarte Flaechen zaehlen getrennt (4er-Nachbarschaft)', () => {
|
||||||
const rows = grid(40, 40, 30);
|
const rows = grid(300, 300, 30);
|
||||||
// Zwei 5x5-Quadrate, die sich nur in einer Ecke (ein Bildpunkt) beruehren.
|
// Zwei 15x15-Quadrate an ihrer naechsten Ecke durch eine 3 Bildpunkte
|
||||||
paint(rows, 10, 10, 5, 5, 220);
|
// breite Luecke getrennt - nah genug, um wie ein einziges, diagonal
|
||||||
paint(rows, 15, 15, 5, 5, 220);
|
// ausgerichtetes Fundstueckpaar auszusehen, aber tatsaechlich getrennt.
|
||||||
|
// Die Luecke ist bewusst breiter als beim alten, Otsu-basierten Test
|
||||||
|
// (dort genuegte ein einzelnes gemeinsames Eckpixel): Die Glaettung
|
||||||
|
// (Schritt 3) dehnt jede Flaeche an ihrem Rand um ein bis zwei Bildpunkte
|
||||||
|
// auf, ein einzelnes gemeinsames Eckpixel wuerde die beiden Flaechen nach
|
||||||
|
// der Glaettung faelschlich zu einer verschmelzen lassen.
|
||||||
|
paint(rows, 130, 130, 15, 15, 220);
|
||||||
|
paint(rows, 148, 148, 15, 15, 220);
|
||||||
const result = countObjects(image(rows));
|
const result = countObjects(image(rows));
|
||||||
assert.equal(result.count, 2, 'nur diagonal beruehrende Flaechen bleiben getrennt');
|
assert.equal(result.count, 2, 'nur diagonal benachbarte Flaechen bleiben getrennt');
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user