In meiner App versuche ich, mithilfe von Open CV eine Gesichtserkennung für ein bestimmtes Bild durchzuführen. Hier trainiere ich zuerst ein Bild und nach dem Training dieses Bildes, wenn ich die Gesichtserkennung für dieses Bild durchführe, erkennt es das trainierte Gesicht erfolgreich. Wenn ich mich jedoch einem anderen Bild derselben Person zuwende, funktioniert die Erkennung nicht. Es funktioniert nur mit dem trainierten Bild. Meine Frage ist also, wie ich es korrigieren kann.
Update: Ich möchte, dass der Benutzer das Bild einer Person aus dem Speicher auswählt. Nach dem Training des ausgewählten Bildes möchte ich alle Bilder aus dem Speicher abrufen, die dem Gesicht meines trainierten Bildes entsprechen
Hier ist meine Aktivitätsklasse:
public class MainActivity extends AppCompatActivity {
private Mat rgba,gray;
private CascadeClassifier classifier;
private MatOfRect faces;
private ArrayList<Mat> images;
private ArrayList<String> imagesLabels;
private Storage local;
ImageView mimage;
Button prev,next;
ArrayList<Integer> imgs;
private int label[] = new int[1];
private double predict[] = new double[1];
Integer pos = 0;
private String[] uniqueLabels;
FaceRecognizer recognize;
private boolean trainfaces() {
if(images.isEmpty())
return false;
List<Mat> imagesMatrix = new ArrayList<>();
for (int i = 0; i < images.size(); i++)
imagesMatrix.add(images.get(i));
Set<String> uniqueLabelsSet = new HashSet<>(imagesLabels); // Get all unique labels
uniqueLabels = uniqueLabelsSet.toArray(new String[uniqueLabelsSet.size()]); // Convert to String array, so we can read the values from the indices
int[] classesNumbers = new int[uniqueLabels.length];
for (int i = 0; i < classesNumbers.length; i++)
classesNumbers[i] = i + 1; // Create incrementing list for each unique label starting at 1
int[] classes = new int[imagesLabels.size()];
for (int i = 0; i < imagesLabels.size(); i++) {
String label = imagesLabels.get(i);
for (int j = 0; j < uniqueLabels.length; j++) {
if (label.equals(uniqueLabels[j])) {
classes[i] = classesNumbers[j]; // Insert corresponding number
break;
}
}
}
Mat vectorClasses = new Mat(classes.length, 1, CvType.CV_32SC1); // CV_32S == int
vectorClasses.put(0, 0, classes); // Copy int array into a vector
recognize = LBPHFaceRecognizer.create(3,8,8,8,200);
recognize.train(imagesMatrix, vectorClasses);
if(SaveImage())
return true;
return false;
}
public void cropedImages(Mat mat) {
Rect rect_Crop=null;
for(Rect face: faces.toArray()) {
rect_Crop = new Rect(face.x, face.y, face.width, face.height);
}
Mat croped = new Mat(mat, rect_Crop);
images.add(croped);
}
public boolean SaveImage() {
File path = new File(Environment.getExternalStorageDirectory(), "TrainedData");
path.mkdirs();
String filename = "lbph_trained_data.xml";
File file = new File(path, filename);
recognize.save(file.toString());
if(file.exists())
return true;
return false;
}
private BaseLoaderCallback callbackLoader = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch(status) {
case BaseLoaderCallback.SUCCESS:
faces = new MatOfRect();
//reset
images = new ArrayList<Mat>();
imagesLabels = new ArrayList<String>();
local.putListMat("images", images);
local.putListString("imagesLabels", imagesLabels);
images = local.getListMat("images");
imagesLabels = local.getListString("imagesLabels");
break;
default:
super.onManagerConnected(status);
break;
}
}
};
@Override
protected void onResume() {
super.onResume();
if(OpenCVLoader.initDebug()) {
Log.i("hmm", "System Library Loaded Successfully");
callbackLoader.onManagerConnected(BaseLoaderCallback.SUCCESS);
} else {
Log.i("hmm", "Unable To Load System Library");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, callbackLoader);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
prev = findViewById(R.id.btprev);
next = findViewById(R.id.btnext);
mimage = findViewById(R.id.mimage);
local = new Storage(this);
imgs = new ArrayList();
imgs.add(R.drawable.jonc);
imgs.add(R.drawable.jonc2);
imgs.add(R.drawable.randy1);
imgs.add(R.drawable.randy2);
imgs.add(R.drawable.imgone);
imgs.add(R.drawable.imagetwo);
mimage.setBackgroundResource(imgs.get(pos));
prev.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(pos!=0){
pos--;
mimage.setBackgroundResource(imgs.get(pos));
}
}
});
next.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(pos<5){
pos++;
mimage.setBackgroundResource(imgs.get(pos));
}
}
});
Button train = (Button)findViewById(R.id.btn_train);
train.setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onClick(View view) {
rgba = new Mat();
gray = new Mat();
Mat mGrayTmp = new Mat();
Mat mRgbaTmp = new Mat();
classifier = FileUtils.loadXMLS(MainActivity.this);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
imgs.get(pos));
Bitmap bmp32 = icon.copy(Bitmap.Config.ARGB_8888, true);
Utils.bitmapToMat(bmp32, mGrayTmp);
Utils.bitmapToMat(bmp32, mRgbaTmp);
Imgproc.cvtColor(mGrayTmp, mGrayTmp, Imgproc.COLOR_BGR2GRAY);
Imgproc.cvtColor(mRgbaTmp, mRgbaTmp, Imgproc.COLOR_BGRA2RGBA);
/*Core.transpose(mGrayTmp, mGrayTmp); // Rotate image
Core.flip(mGrayTmp, mGrayTmp, -1); // Flip along both*/
gray = mGrayTmp;
rgba = mRgbaTmp;
Imgproc.resize(gray, gray, new Size(200,200.0f/ ((float)gray.width()/ (float)gray.height())));
if(gray.total() == 0)
Toast.makeText(getApplicationContext(), "Can't Detect Faces", Toast.LENGTH_SHORT).show();
classifier.detectMultiScale(gray,faces,1.1,3,0|CASCADE_SCALE_IMAGE, new Size(30,30));
if(!faces.empty()) {
if(faces.toArray().length > 1)
Toast.makeText(getApplicationContext(), "Mutliple Faces Are not allowed", Toast.LENGTH_SHORT).show();
else {
if(gray.total() == 0) {
Log.i("hmm", "Empty gray image");
return;
}
cropedImages(gray);
imagesLabels.add("Baby");
Toast.makeText(getApplicationContext(), "Picture Set As Baby", Toast.LENGTH_LONG).show();
if (images != null && imagesLabels != null) {
local.putListMat("images", images);
local.putListString("imagesLabels", imagesLabels);
Log.i("hmm", "Images have been saved");
if(trainfaces()) {
images.clear();
imagesLabels.clear();
}
}
}
}else {
/* Bitmap bmp = null;
Mat tmp = new Mat(250, 250, CvType.CV_8U, new Scalar(4));
try {
//Imgproc.cvtColor(seedsImage, tmp, Imgproc.COLOR_RGB2BGRA);
Imgproc.cvtColor(gray, tmp, Imgproc.COLOR_GRAY2RGBA, 4);
bmp = Bitmap.createBitmap(tmp.cols(), tmp.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(tmp, bmp);
} catch (CvException e) {
Log.d("Exception", e.getMessage());
}*/
/* mimage.setImageBitmap(bmp);*/
Toast.makeText(getApplicationContext(), "Unknown Face", Toast.LENGTH_SHORT).show();
}
}
});
Button recognize = (Button)findViewById(R.id.btn_recognize);
recognize.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(loadData())
Log.i("hmm", "Trained data loaded successfully");
rgba = new Mat();
gray = new Mat();
faces = new MatOfRect();
Mat mGrayTmp = new Mat();
Mat mRgbaTmp = new Mat();
classifier = FileUtils.loadXMLS(MainActivity.this);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
imgs.get(pos));
Bitmap bmp32 = icon.copy(Bitmap.Config.ARGB_8888, true);
Utils.bitmapToMat(bmp32, mGrayTmp);
Utils.bitmapToMat(bmp32, mRgbaTmp);
Imgproc.cvtColor(mGrayTmp, mGrayTmp, Imgproc.COLOR_BGR2GRAY);
Imgproc.cvtColor(mRgbaTmp, mRgbaTmp, Imgproc.COLOR_BGRA2RGBA);
/*Core.transpose(mGrayTmp, mGrayTmp); // Rotate image
Core.flip(mGrayTmp, mGrayTmp, -1); // Flip along both*/
gray = mGrayTmp;
rgba = mRgbaTmp;
Imgproc.resize(gray, gray, new Size(200,200.0f/ ((float)gray.width()/ (float)gray.height())));
if(gray.total() == 0)
Toast.makeText(getApplicationContext(), "Can't Detect Faces", Toast.LENGTH_SHORT).show();
classifier.detectMultiScale(gray,faces,1.1,3,0|CASCADE_SCALE_IMAGE, new Size(30,30));
if(!faces.empty()) {
if(faces.toArray().length > 1)
Toast.makeText(getApplicationContext(), "Mutliple Faces Are not allowed", Toast.LENGTH_SHORT).show();
else {
if(gray.total() == 0) {
Log.i("hmm", "Empty gray image");
return;
}
recognizeImage(gray);
}
}else {
Toast.makeText(getApplicationContext(), "Unknown Face", Toast.LENGTH_SHORT).show();
}
}
});
}
private void recognizeImage(Mat mat) {
Rect rect_Crop=null;
for(Rect face: faces.toArray()) {
rect_Crop = new Rect(face.x, face.y, face.width, face.height);
}
Mat croped = new Mat(mat, rect_Crop);
recognize.predict(croped, label, predict);
int indice = (int)predict[0];
Log.i("hmmcheck:",String.valueOf(label[0])+" : "+String.valueOf(indice));
if(label[0] != -1 && indice < 125)
Toast.makeText(getApplicationContext(), "Welcome "+uniqueLabels[label[0]-1]+"", Toast.LENGTH_SHORT).show();
else
Toast.makeText(getApplicationContext(), "You're not the right person", Toast.LENGTH_SHORT).show();
}
private boolean loadData() {
String filename = FileUtils.loadTrained();
if(filename.isEmpty())
return false;
else
{
recognize.read(filename);
return true;
}
}
}
Meine Datei Utils Klasse:
public class FileUtils {
private static String TAG = FileUtils.class.getSimpleName();
private static boolean loadFile(Context context, String cascadeName) {
InputStream inp = null;
OutputStream out = null;
boolean completed = false;
try {
inp = context.getResources().getAssets().open(cascadeName);
File outFile = new File(context.getCacheDir(), cascadeName);
out = new FileOutputStream(outFile);
byte[] buffer = new byte[4096];
int bytesread;
while((bytesread = inp.read(buffer)) != -1) {
out.write(buffer, 0, bytesread);
}
completed = true;
inp.close();
out.flush();
out.close();
} catch (IOException e) {
Log.i(TAG, "Unable to load cascade file" + e);
}
return completed;
}
public static CascadeClassifier loadXMLS(Activity activity) {
InputStream is = activity.getResources().openRawResource(R.raw.lbpcascade_frontalface);
File cascadeDir = activity.getDir("cascade", Context.MODE_PRIVATE);
File mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface_improved.xml");
FileOutputStream os = null;
try {
os = new FileOutputStream(mCascadeFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
is.close();
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return new CascadeClassifier(mCascadeFile.getAbsolutePath());
}
public static String loadTrained() {
File file = new File(Environment.getExternalStorageDirectory(), "TrainedData/lbph_trained_data.xml");
return file.toString();
}
}
Dies sind die Bilder, die ich hier zu vergleichen versuche. Das Gesicht einer Person ist immer noch das gleiche, aber es ist nicht passend!
quelle
Antworten:
Aktualisieren
Entsprechend der neuen Bearbeitung in der Frage benötigen Sie eine Möglichkeit, neue Personen im laufenden Betrieb zu identifizieren, deren Fotos während der Trainingsphase des Modells möglicherweise nicht verfügbar waren. Diese Aufgaben werden als Wenig-Schuss-Lernen bezeichnet . Dies ähnelt den Anforderungen der Geheimdienste / Polizeibehörden, ihre Ziele mithilfe von CCTV-Kameraaufnahmen zu finden. Da normalerweise nicht genügend Bilder eines bestimmten Ziels vorhanden sind, werden während des Trainings Modelle wie FaceNet verwendet . Ich empfehle wirklich, die Zeitung zu lesen, erkläre hier jedoch einige ihrer Höhepunkte:
Eine Implementierung von FaceNet finden Sie hier . Ich schlage vor, Sie versuchen, es auf Ihrem Computer auszuführen, um zu erfahren, womit Sie sich tatsächlich befassen. Danach ist es möglicherweise am besten, Folgendes zu tun:
Ursprüngliche Antwort
Sie sind auf eine der häufigsten Herausforderungen des maschinellen Lernens gestoßen: Überanpassung. Die Gesichtserkennung und -erkennung ist ein riesiges Forschungsgebiet für sich und fast alle einigermaßen genauen Modelle verwenden eine Art tiefes Lernen. Beachten Sie, dass selbst das genaue Erkennen eines Gesichts nicht so einfach ist, wie es scheint. Wenn Sie es jedoch auf Android ausführen, können Sie für diese Aufgabe die Gesichts-API verwenden . (Andere fortgeschrittenere Techniken wie MTCNN sind zu langsam / schwierig auf einem Mobilteil bereitzustellen .) Es hat sich gezeigt, dass es nicht funktioniert, das Modell nur mit einem Gesichtsfoto mit vielen Hintergrundgeräuschen oder mehreren Personen zu füttern. Sie können diesen Schritt also wirklich nicht überspringen.
Nachdem Sie ein schönes, zugeschnittenes Gesicht der Kandidatenziele aus dem Hintergrund erhalten haben, müssen Sie die Herausforderung bewältigen, die erkannten Gesichter zu erkennen. Wiederum verwenden alle kompetenten Modelle nach meinem besten Wissen eine Art Deep Learning / Convolutional Neural Networks. Die Verwendung auf einem Mobiltelefon ist eine Herausforderung, aber dank Tensorflow Lite können Sie sie minimieren und in Ihrer App ausführen. Ein Projekt zur Gesichtserkennung auf Android-Handys, an dem ich gearbeitet habe, ist das Transferlernen . Für einen schnellen Einstieg in die Objekterkennung und das Transferlernen, das eng mit Ihrem Fall zusammenhängt, lesen Sie diesen Blog-Beitrag. hier , das Sie überprüfen können. Denken Sie daran, dass jedes gute Modell auf zahlreiche Instanzen gekennzeichneter Daten trainiert werden sollte. Es gibt jedoch eine Vielzahl von Modellen, die bereits auf großen Datenmengen von Gesichtern oder anderen Bilderkennungsaufgaben trainiert wurden, um sie zu optimieren und ihr vorhandenes Wissen zu nutzen, das wir einsetzen können
Insgesamt müssen Sie zahlreiche Instanzen der Gesichter erhalten, die Sie erkennen möchten, sowie zahlreiche Gesichtsbilder von Personen, die Sie nicht interessieren. Dann müssen Sie ein Modell trainieren, das auf den oben genannten Ressourcen basiert, und dann müssen Sie Verwenden Sie TensorFlow lite, um die Größe zu verringern und in Ihre App einzubetten. Für jeden Frame rufen Sie dann die Android Face API auf und geben (das wahrscheinlich erkannte Gesicht) in das Modell ein und identifizieren die Person.
Abhängig von Ihrer Toleranz für Verzögerungen und der Anzahl der Trainingssätze und der Anzahl der Ziele können Sie verschiedene Ergebnisse erzielen. Eine Genauigkeit von% 90 + ist jedoch leicht zu erreichen, wenn Sie nur wenige Zielpersonen haben.
quelle
Wenn ich das richtig verstehe, trainierst du den Klassifikator mit einem einzigen Bild. In diesem Fall ist dieses eine spezifische Bild alles, was der Klassifizierer jemals erkennen kann. Sie benötigen einen deutlich größeren Trainingssatz mit Bildern, die dieselbe Person zeigen, mindestens 5 oder 10 verschiedene Bilder.
quelle
train()
: docs.opencv.org/3.4/dd/d65/…1) Ändern Sie den Schwellenwert beim Initialisieren von LBPHrecognizer auf -> LBPHFaceRecognizer (1, 8, 8, 8, 100).
2) Trainiere jedes Gesicht mit mindestens 2-3 Bildern, da diese Erkenner hauptsächlich im Vergleich arbeiten
3) Stellen Sie die Genauigkeitsschwelle beim Erkennen ein. Mach so etwas:
quelle