Legostein Kantenerkennung

Hallo Leute,

Ich schaue seit Wochen und komme ich nicht voran. Ich muss ein Android Projekt machen

Die Applikation soll anhand eines Fotos des Bausteins, welches der Benutzer selber aufnimmt, seine eigens aufgebaute Datenbank abgleichen und erkennen, um welchen Baustein es sich handelt (unter Verwendung der offiziellen Lego ID Nummern). Wurde der Baustein identifiziert, so soll es für den Benutzer möglich sein diesen Stein in seine persönliche Sammlung aufzunehmen und die vorhandene Anzahl dieses Bausteins einzugeben. Ist der forografierte Baustein nicht in der Datenbank enthalten, so soll es dem Benutzer ermöglicht werden, diesen in die Datenbank aufzunehmen. In diesem Fall muss der Benutzer alle Attribute des Bausteins selber in die Datenbank eingeben. Die Applikation soll in der Lage sein die Farbe und ungefähre Form der gängigsten Bausteine zu erkennen.
Neben dem Erkennen und Einfügen von Bausteinen, soll der Benutzer auch die Möglichkeit haben sich seine persönliche Sammlung anzusehen. Dazu würde der Benutzer weitere Informationen, wie z.B. die Gesamtanzahl an vorhandenen Bausteinen, bekommen.

so schaut mein Aktueller code aus

import android.view.View;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.provider.MediaStore;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.core.Mat;

public class MainActivity extends Activity implements CvCameraViewListener2 {

	public boolean mainIsOpen = true;

	public static final int REQUEST_IMAGE_CAPTURE = 1;


	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

	}

	public void addLego(View view) {
		Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

		if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
			startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
		}
	}

	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();

	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		// TODO Auto-generated method stub
		if (keyCode == KeyEvent.KEYCODE_BACK && mainIsOpen == false) {
			mainIsOpen = true;
			setContentView(R.layout.activity_main);
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();

	}

	@Override
	public void onCameraViewStarted(int width, int height) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onCameraViewStopped() {
		// TODO Auto-generated method stub

	}

	@Override
	public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
		// TODO Auto-generated method stub
		return null;
	}

}

ich denke dass ich in der Methode onCameraFrame eine Algorithmus haben muss wo ich längen und breiten rauslesen kann.
Ich habe geschaut und für den Kantenerkennung cany Algorithmus gefunden , aber leider nur in c++
denkt ihr dass ich den Code auch in Java umsetzen kann oder umsonst werde ich jede Zeile googeln?

auf eure Antwort wurde ich mich wirklich sehr freuen

[QUOTE=anni80]Ich habe geschaut und für den Kantenerkennung cany Algorithmus gefunden , aber leider nur in c++
denkt ihr dass ich den Code auch in Java umsetzen kann oder umsonst werde ich jede Zeile googeln?[/quote]Canny & Co sind ziemlich simple Dinger, die kann man problemlos auch in Java formulieren. Schau dir doch mal an was Canny eigentlich genau macht, dann brauchst du wahrscheinlich schon keinen vorgegebenen Code mehr sondern kannst das auch selber implementieren.

[QUOTE=anni80;106753]ich denke dass ich in der Methode onCameraFrame eine Algorithmus haben muss wo ich längen und breiten rauslesen kann.[/quote]Gibt es bestimmte Vorgaben, die das Foto erfüllen muss? Maße aus einem beliebigen 2D-Bild herauszulesen ist nicht ganz ohne. Falls es nur um Standardbausteine geht könnte es einfacher sein die Anzahl der “Noppen” zu bestimmen.

danke dir. extstremst nett. Ja es muss standardbaustein sein. muss ich jetzt andere Richtung suchen? Brauche ich da andere Algorithmus? soll man int noppe[][] als einzelner stein so speichern?

Für Java SE steht ein Beispiel unter https://docs.oracle.com/javase/8/docs/technotes/guides/2d/spec/j2d-image.html#wp61818 . Für Android… tja… Websuche liefert da aber einiges, wenn man die passenden Begriffe dazuwirft, z.B. Image Processing – Convolution Matrix | [ Android Newbie ] (In dem Link, den du gepostet hast, wird so ein Filter ja nur verwendet, um die Kantenerkennung zu machen). Man hat damit aber erstmal keine “semantische” Information. Man weiß nur, wo die Kanten sind. Zu erkennen, welche der Kanten zum Stein gehören, ist alles andere als trivial… xkcd: Tasks

ich glaube wie @Dow_Jones sagte Noppenanzahlerkennung ist machbarer. Ich werde nächste 2 Tage herumgoogeln und werde ich euch hier noch mal überfallen

es ist ganz ganz blöde frage, aber als ich Richtung kantenerkennung gedacht habe, habe ich edge detection opencv gegoogelt und habe canny Algorithmus gefunden. habt ihr eine Idee und Erfahrung wie man noppen zählen soll? wenn ich danach google kommt gar nichts nützliches. Kann mir jemand eine Anweisung geben wie ich vorgehen sollte?

Ich bin bei dem “Noppen zählen” etwas skeptisch. In gewisser Hinsicht ist das Problem das gleiche: Auch dort müßte man Kanten erkennen. Danach bräuchte man eigentlich sowieso noch eine Hough-Transformation – Wikipedia um den Pixeldaten eine “Semantik” zu geben - und DANN ist schon fast egal, ob es Kreise oder Linien sind. In allen Fällen hat man das Problem, dass es praktisch UN(!)möglich ist, den Stein zu identifizieren, wenn er “beliebig” aufgenommen sein kann. Wenn man z.B. genau von oben draufschaut, erkennt man zwar die Noppen als perfekte Kreise, aber man erkennt die Höhe des Steins nicht. Wenn man von unten draufschaut, erkennt man die Noppen nicht. Ich denke, irgendeine Einschränkung (im Sinne einer groben Anleitung, wie der Stein bei der Aufnahme zu liegen hat) wird da auf jeden Fall notwendig sein.

nehmen wir an, dass ich darauf einschränke dass man von oben Fotografieren kann. Sollte ich dann cantenerkennungsalgorithmus machen und das vild filtern und dann die Kreise Zählen?

Wie gesagt: Wenn man perfkekt von oben fotografiert, erkennt man die Noppen recht gut, und kann die relativ (!) einfach zählen. Ich hatte überlegt, ob man dann nicht auch genauso einfach (oder noch einfacher) aus den Längenverhältnissen der Seiten ableiten könnte, welcher Stein es sein muss, aber ob das immer möglich ist, müßte man sich genauer überlegen. Ich denke, das ist eine Aufgabe, die bewußt “open-end” ist: KEINER wird es schaffen, ALLE Steine aus JEDER Blickrichtung zu erkennen (schon weil das technisch gar nicht möglich ist). Man kann also nur versuchen, es so gut und flexibel wie möglich zu machen. Konkrete Hinweise, im Sinne einer definitiv-verbindlich-fundierten Aussage wie “Du MUSST Noppen zählen” oder “Du MUSST die Haupt-Kanten erkennen” sind darum schwierig…

Da du ja schon nach Canny fragtest möchte ich mal nachhaken: In wieweit bist du bereits auf diesem Gebiet (Computervision, Mustererkennung etc.) bewandert, Anni? Die Aufgabe einen Legostein zuverlässig auf einem Foto zu identifizieren - das schüttelt man nicht mal eben aus dem Ärmel.

Eine mögliche Standardvorgehensweise dafür wäre:

  • sich irgendwelche, mehr oder weniger abstrakten, Merkmale* überlegen, die ein Foto eines Legosteins aufweisen kann. Als Merkmal kann alles erdenkliche herhalten, gerade auch Dinge die man als Mensch nicht für naheliegend hält.
  • man besorgt sich eine Stichprobe, also ca. 1000 Fotos von Legosteinen und ca. 1000 weitere Fotos welche keine Legosteine zeigen
  • man lässt den Computer aus jedem Foto der Stichprobe die Merkmale extrahieren und erhält dadurch eine Menge von Merkmalswerten (den sog. Merkmalsvektor im Merkmalsraum)
  • anschließend soll sich der Computer selber überlegen, wie er die Merkmalsvektoren der Fotos mit den Legosteinen von den Merkmalsvektoren der Fotos ohne Legosteine unterscheidet (aka Klassifikationsproblem). Dafür gibt’s zum Glück 'ne Reihe bekannter Algorithmen.

Für ein solches Vorgehen müsste man allerdings schon eine gute Portion Fachwissen mitbringen, sonst wird’s ein steiniger Weg…

Da Legosteine gewisse Eigenschaften haben, die auch gültig bleiben wenn man den Stein aus Perspektiven betrachtet, fällt mir alternativ gerade ein:

  • das Foto zunächst mal segmentieren, um alles, was nicht zum Legostein gehört, herauszulöschen. Das böte sich an, da Legosteine - soweit ich mich erinnere - einfarbig sind, und zwar in charakteristischen Farben. Die einzelnen Pixel des Steins (bzw. der Seitenflächen des Steins) sollten sich lediglich durch die Beleuchtung unterscheiden. Da müsste eine Segmentierung im HSV-Raum eigentlich gute Ergebnisse liefern.
  • Kantenbild erzeugen, Hough-Transformation laufen lassen, Kreuzungspunkte zwischen Geraden bestimmen. Daraus könnte man (hoffentlich) schon ableiten ob man die „typischen Kanten“ eines Legosteins gefunden hat. Wenn nicht: Bild verwerfen.
  • im Kantenbild könnte man nun nach den Elipsen der Noppen suchen (oder besser nach den „Halbmonden“, die durch die Schatten der Noppen entstehen?). So eine Elipse wäre dadurch gekennzeichnet, das sie einen Mittelpunkt hat, für den im Idealfall gilt: Wenn in Richtung r und in Entfernung e ein Pixel im Kantenbild gesetzt ist, dann muss in der gleichen Entfernung auch in Richtung -r ein Pixel gesetzt sein. Diese Bedingung müsste man halt für jeden Pixel, jede Richtung und jede Entfernung prüfen. Dauert zwar ewig, aber wenn’s funktioniert kann man sich sicher noch Optimierungen überlegen.

Hmm. Ist alles sehr vage, aber wie gesagt, sowas macht man nicht mal eben mit links. Rumprobieren ist angesagt…
Zum Testen verschiedener Ansätze ist Matlab** übrigens sehr zu empfehlen. Dessen Skriptsprache ist zwar erstmal gewöhnungsbedürftig, aber wenn man die erstmal einigermaßen kann, dann geht das Arbeiten damit seeehr viel flotter von der Hand als wenn man alles selber in Java implementieren müsste. Matlab & Co können von Haus aus schon sehr viel, Lösungen für so ziemlich alles weitere findet man im Netz. Für’s „prototyping“ möchte ich’s nicht missen. :slight_smile:

  • als Merkmal kann alles Mögliche dienen, z.B. die Anzahl der (durch eine Houghtransformation) ermittelten Geraden/Elipsen im Bild. Allerdings wäre das schon sehr „high-level“, meistens bedient man sich viel simplerer/abstrakterer (und dadurch universellerer) Merkmale, siehe z.B. SIFT, SURF, Viola-Jones o. ä.

** falls du Matlab nicht über deinen Arbeitgeber/Hochschule bekommst gibt es auch eine Reihe äquivalenter, kostenfreier und z.T. auch befehlskompatibler Alternativen, z.B. FreeMat, SciLab oder Octave.

[QUOTE=Dow_Jones;106789]…, sonst wird’s ein steiniger Weg…
[/QUOTE]

Pun intended? :smiley:

Liebe Leute,

@Dow_Jones : danke für deine ausführliche Antwort. Werde ich schauen, dass ich deine Ratschläge implementieren kann

ich versuche gray Bild zu bekommen, aber liefert diese Methode trotzdem RGB Bild

	public Mat onCameraFrame(CvCameraViewFrame inputFrame) {

		final int viewMode = mViewMode;
		int iCannyLowerThreshold;
		int iCannyUpperThreshold;
		
			Imgproc.cvtColor(mRgba, mGray, Imgproc.COLOR_RGBA2GRAY);

			iCannyLowerThreshold = 35;
			iCannyUpperThreshold = 75;

			Imgproc.Canny(mGray, mIntermediateMat, iCannyLowerThreshold,
					iCannyUpperThreshold);

			Imgproc.cvtColor(mIntermediateMat, mRgba, Imgproc.COLOR_GRAY2BGRA,
					4);

			
		return mGray;

	}```


wo habe ich Fehler?

OpenCV habe ich bislang nie groß verwendet; k.A. wie man dort zu einem Grauwertbild gelangt. Falls du irgendwie an ein „normales“ BufferedImage kommst könntest du die Hausmittel von Java verwenden, damit klappt das prima (siehe Convert a Color Image to a Gray Scale Image in Java).

Ich habe mir die ganze Angelegenheit gerade mal genauer angeschaut, und vielleicht wird der Standardweg gar nicht so steinig. :slight_smile: (@Marco: nein, war’s gar nicht mal; aber ich schätze Sigmund freut’s…)

Wie schon vermutet lassen sich Legosteine auf einem Foto im HSV-Bereich gut voneinander unterscheiden.

Gut genug für eine Segmentierung jedenfalls, denn die muss eigentlich nicht besonders exakt werden.

Wenn man nun einen Tiefpass auf den Value-Kanal anwendet erhält man folgende Bilder (oben Original, unten TP-gefilterte Values):

Sieht nicht besonders spektakulär aus. Aber wenn man sich die TP-Valuebilder mal in 3D ansieht, also mit dem Grauwert als Höhenkoordinate, dann lassen sich die Noppen und Seitenwände ganz wunderbar erkennen.

Da würde es sich doch anbieten auf Canny & Co zu verzichten und stattdessen einfach im Valuebild nach lokalen Maxima (oder Minima, falls die Noppen, je nach Beleuchtung, Schatten erzeugen) zu suchen. Die geben prima offenbar Merkmale ab, welche man zur Klassifikation nutzen kann. Wenn auf dem Foto außer den Legosteinen sonst nichts zu sehen ist dann kann man das wahrscheinlich sogar von Hand machen: Man suche nach Maxima, die alle gleich hoch sind und in Reih und Glied stehen. Die Farbe des Legosteins lässt sich durch den Hue-Wert separat bestimmen. Die Höhe des Steins - hmm, da müsste man sich noch was überlegen. Dazu wäre vielleicht doch ein Kantenbild nützlich.

Darf ich nach dem Hintergrund der Aufgabe fragen? Job? Studium? Hobby? Wieviel Zeit hast du dafür? (und wärst du bereit/interessiert dich weiter in das Thema Mustererkennung einzulesen…?)

Hallo,

ich mache das privat und habe ich vor 2 bis 3 Stunden am Tag zu investieren.

Ich habe Kantenerkennung gegoogelt und hat mir canny Algorithmus rausgeschlückt.

Für den Anfang möchte ich auf obere Seite von Lego beschränken. Entweder denke ich dass ich Verhältnis von Breite und länge speichern oder noppenanzahl in Array

Nächte Wochen werde ich mit Datenbanken beschäftigen und dann komme ich nochmal zu den Kanten.

Was empfiehlst du mir wenn ich openCV auf die Seite gebe? Du hast von Matlab empfohlen. Kann man die aus Matlab Messungen in android importieren? Wenn das wirklich machbar ist wurde in Matlab einarbeiten

Da ich von OpenCV, wie schon erwähnt, keinen Plan habe, kann ich dir dazu leider auch nichts empfehlen. -_-

Man kann Matlab-Skripte wohl irgendwie kompilieren und von anderen Sprachen aus aufrufen, aber auch damit habe ich mich noch nie auseinandergesetzt… Empfohlen habe ich es, weil das Analysieren von Daten und Ausprobieren von Vorgehensweisen damit unheimlich leicht von der Hand geht. Und beides, Analysieren und Ausprobieren, wird man ausgiebig tun müssen, bevor man eine einigermaßen robuste Erkennung von Legosteinen hinbekommt. Erst wenn man das geschafft hat lohnt es sich meiner Ansicht nach sich hinzusetzen um das Ganze in Java/OpenCV implementieren.

Hallo Leute,

ich habe diesen Link gefunden
ich denke mir dass es in meinem Fall sinnvoll sein kann die Kreise zu zählen
ich möchte in java folgende schritte realisieren

// Convert to grayscale
cv::Mat gray;
cv::cvtColor(src, gray, CV_BGR2GRAY);

// Convert to binary image using Canny
cv::Mat bw;
cv::Canny(gray, bw, 0, 50, 5);

// Detect and label circles
double area = cv::contourArea(contours**);
cv::Rect r = cv::boundingRect(contours**);
int radius = r.width / 2;

    if (std::abs(1 - ((double)r.width / r.height)) <= 0.2 &&
        std::abs(1 - (area / (CV_PI * std::pow(radius, 2)))) <= 0.2)
    {
        setLabel(dst, "CIR", contours**);
    }

wenn ich versuche als Graubilder zu speichern mit folgende Code und in gespeicherte Bild ist das nur originalbild. Wo liegt mein Fehler?

		
		Imgproc.Canny(inputFrame.gray(), mIntermediateMat, 80, 100);
        Imgproc.cvtColor( mIntermediateMat, mRgba,
                          Imgproc.COLOR_RGBA2GRAY, 4 );
		return mRgba;
	}```