Açık CV Yüz Tanıma doğru değil


13

Benim app Açık CV kullanarak belirli bir görüntü üzerinde yüz tanıma yapmaya çalışıyorum, burada önce bir görüntü eğitim ve daha sonra bu görüntü üzerinde yüz tanıma çalıştırırsanız o eğitimli yüz başarıyla tanır. Ancak, aynı kişinin başka bir resmine döndüğümde tanıma çalışmaz. Sadece eğitimli görüntü üzerinde çalışıyor, bu yüzden sorum şu ki nasıl düzeltebilirim?

Güncelleme: Yapmak istediğim şey, kullanıcının depolama alanından bir kişinin resmini seçmesi ve daha sonra bu seçilen resmi eğittikten sonra, eğitilmiş resmimin yüzüyle eşleşen tüm resimleri depolamadan almak istiyorum

İşte benim aktivite sınıfım:

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;
        }
    }
}

My File Utils Sınıfı:

   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();
        }
    }

Bunlar burada karşılaştırmaya çalıştığım görüntüler kişinin yüzü hala aynı tanıma eşliğinde! Resim 1 Resim 2


Otomatik Katılım Sistemi için son yıl görevimi oluşturduğumda, sınıflandırıcıyı eğitmek için kendimi 8-10 farklı pozlar ve ışık koşulları olan görüntüler kullandım.
ZdaR

Bu gereksinimi karşılamak için egzersiz görüntü matınızı yatay olarak çevirebilirsiniz.
nfl-x

@ nfl-x çevirme görüntüleri doğruluk sorununu çözmeyecek tensorflow'da daha yeni bir cevaba ihtiyacımız var tamam görünüyor ama android için uygulanmasında yeterli bilgi veya öğretici mevcut değil, bu yüzden en iyi tahminimiz bu yazıyı oylamaya devam etmektir böylece bir uzman müdahale edebilir ve android için uygun bir çözüm sağlayabilir
Bay Patel

Yanıtlar:


5

Güncelleme

Sorudaki yeni düzenlemeye göre, modelin eğitim aşamasında fotoğrafları bulunamayan yeni kişileri anında tanımlamanın bir yoluna ihtiyacınız var. Bu görevlere çok az atış öğrenme denir . Bu, istihbarat / polis teşkilatlarının CCTV kamera görüntülerini kullanarak hedeflerini bulma gereksinimlerine benzer. Genellikle belirli bir hedefin yeterli görüntüsü olmadığından, eğitim sırasında FaceNet gibi modeller kullanırlar . Makaleyi gerçekten okumanızı öneririm, ancak burada öne çıkan birkaçını açıklıyorum:

  • Genel olarak, bir sınıflandırıcının son katmanı, elemanların n-1'i neredeyse sıfıra ve biri 1'e yakın olan bir * 1 vektörüdür. 1'e yakın eleman, sınıflandırıcının giriş etiketi hakkındaki tahminini belirler. Tipik CNN mimarisi
  • Yazarlar, büyük bir yüz veri kümesinde belirli bir kayıp fonksiyonu olan bir sınıflandırıcı ağını eğitirlerse, yarı final katman çıktısını, eğitim setinde olmasına bakılmaksızın, herhangi bir yüzün temsili olarak kullanabileceğinizi anladılar, yazarlar bu vektör Yüz Gömme diyorlar .
  • Önceki sonuç, çok iyi eğitilmiş bir FaceNet modeliyle, herhangi bir yüzü bir vektörde özetleyebileceğiniz anlamına gelir. Bu yaklaşımın en ilginç özelliği, belirli bir kişinin yüzünün farklı açılar / konumlar / durumlardaki vektörlerinin öklid uzayında yakın olmasıdır (bu özellik, yazarların seçtiği kayıp fonksiyonu tarafından uygulanır).resim açıklamasını buraya girin
  • Özetle, yüzleri girdi olarak alan ve vektörleri döndüren bir modeliniz var. Birbirine yakın vektörlerin aynı kişiye ait olması muhtemeldir (KNN veya sadece basit öklid mesafesini kullanabileceğinizi kontrol etmek için).

FaceNet'in bir uygulaması burada bulunabilir . Aslında neyle uğraştığınızı öğrenmek için bilgisayarınızda çalıştırmayı denemenizi öneririm. Bundan sonra, aşağıdakileri yapmak en iyisi olabilir:

  1. Havuzda belirtilen FaceNet modelini tflite sürümüne dönüştürün ( bu blog yazısı yardımcı olabilir)
  2. Kullanıcı tarafından gönderilen her fotoğraf için yüzleri çıkarmak için Yüz API'sını kullanın
  3. Çıkarılan yüzün yüz düğünlerini almak için uygulamanızdaki küçültülmüş modeli kullanın.
  4. Kullanıcının galerisindeki tüm görüntüleri işleyerek fotoğraflardaki yüzler için vektörleri alın.
  5. Daha sonra eşleşmeleri almak için 4. adımda bulunan her vektörü 3. adımda bulunan her vektörle karşılaştırın.

Orijinal Yanıt

Makine öğreniminin en yaygın zorluklarından biriyle karşılaştınız: Aşırı takma. Yüz tanıma ve tanıma tek başına büyük bir araştırma alanıdır ve neredeyse tüm doğru modeller bir tür derin öğrenme kullanır. Bir yüzü doğru bir şekilde tespit etmenin göründüğü kadar kolay olmadığını unutmayın, ancak android'de yaptığınız gibi, bu görev için Face API'yi kullanabilirsiniz . ( MTCNN gibi diğer daha gelişmiş teknikler bir ahizede konuşlandırılmayacak kadar yavaş / zordur). Modeli çok fazla arka plan gürültüsü olan bir yüz fotoğrafı ile veya içeride birden fazla insanla beslemenin işe yaramadığı gösterilmiştir. Yani, bu adımı gerçekten atlayamazsınız.

Aday hedeflerinin arka planından güzel bir şekilde kesilmiş bir yüzünü aldıktan sonra, tespit edilen yüzleri tanıma zorluğunun üstesinden gelmeniz gerekir. Yine, bildiğim kadarıyla tüm yetkin modeller, bir çeşit derin öğrenme / evrişimli sinir ağları kullanıyor. Onları bir cep telefonunda kullanmak zor bir iştir, ancak Tensorflow Lite sayesinde bunları küçültebilir ve uygulamanızda çalıştırabilirsiniz. Ben işe yaradığını android telefonlarda yüz tanıma ile ilgili bir projedir burada sen kontrol edebilirsiniz. Herhangi bir iyi modelin çok sayıda etiketli veri örneği üzerinde eğitilmesi gerektiğini unutmayın, ancak bunları değiştirmek ve mevcut bilgilerini kullanmak için büyük yüz veri kümelerinde veya diğer görüntü tanıma görevlerinde zaten eğitilmiş çok sayıda model vardır.transfer öğrenme , nesne tespiti ve vakanızla yakından ilişkili transfer öğrenmeye hızlı bir başlangıç ​​için bu blog gönderisini kontrol edin .

Genel olarak, tespit etmek istediğiniz yüzlerin sayısız örneğini ve umursadığınız insanların sayısız yüz resmini almanız gerekir, o zaman yukarıda belirtilen kaynaklara dayalı bir model eğitmeniz gerekir ve sonra boyutunu küçültmek ve uygulamanıza gömmek için TensorFlow lite kullanın. Daha sonra her kare için android Face API'yi çağırır ve modele (muhtemelen algılanan yüz) besler ve kişiyi tanımlarsınız.

Gecikmeye karşı tolerans seviyenize ve egzersiz seti boyutuna ve hedef sayısına bağlı olarak, çeşitli sonuçlar alabilirsiniz, ancak yalnızca birkaç hedef kişiniz varsa% 90 + doğruluğu kolayca elde edilebilir.


Uygulamamda ağ bağlantısını kullanmak istemiyorum, bu yüzden google bulut vizyonu söz konusu değil ama tensör akış lite oldukça ilginç görünüyor ücretsiz mi? ve eğer çalışan bir örnek sağlayabilirseniz takdir edeceğim! Thanks
R.Coder

Bu arada harika bir cevap!
R.Coder

Bedava. Çalışan bir örnek için bunu kontrol edin . Kullanıcı deneyimi tarafında bazı aksaklıklar olmasına rağmen, 225 kişinin yüzlerini çok yüksek doğrulukta ağ bağlantısı kullanmadan tanımlayabildik. Ama bu iyi bir başlangıç ​​olmalı.
Farzad Vertigo

Tamam, bir deneyeceğim
R.Coder

1
İşe yaradı!!!! nihayet o yüz net modeli tflite ayıklanmış ve tek bir eğitimli görüntü üzerinde% 80'in üzerinde doğruluk var. ama zaman karmaşıklığı gerçekten çok büyük !!, İki görüntüyü karşılaştırmak için bunu azaltmak için herhangi bir fikir en az 5 ila 6 saniye sürer?
R.Coder

2

Doğru anlarsam, sınıflandırıcıyı tek bir resim ile eğitiyorsunuzdur. Bu durumda, bu belirli bir resim, sınıflandırıcının hiç tanıyamayacağı her şeydir. En az 5 veya 10 farklı görüntü gibi aynı kişiyi gösteren kayda değer ölçüde daha büyük bir eğitim setine ihtiyacınız olacaktır.


Bunun nasıl yapılacağına dair bir örneğiniz var mı?
R.Coder

Evet, tek bir statik görüntüde yüz tanıma yapıyorum
R.Coder

Nasıl kullanılacağıyla ilgili örnek için buraya bakın train(): docs.opencv.org/3.4/dd/d65/…
Florian Echtler

Android ile ilgili bazı kodlanmış örnek sağlayabilirseniz bu cevap yardımcı olmaz, daha iyi olurdu!
R.Coder

0

1) LBPHrecognizer'ı -> LBPHFaceRecognizer olarak başlatırken eşik değerini değiştirin (1, 8, 8, 8, 100)

2) bu yüz tanıtıcıları esas olarak karşılaştırma üzerinde çalıştığı için her yüzü en az 2-3 resim ile eğitin

3) Tanıma sırasında doğruluk eşiğini ayarlayın. Bunun gibi bir şey yapın:

//predicting result
// LoadData is a static class that contains trained recognizer
// _result is the gray frame image captured by the camera
LBPHFaceRecognizer.PredictionResult ER = LoadData.recog.Predict(_result);
int temp_result = ER.Label;

imageBox1.SizeMode = PictureBoxSizeMode.StretchImage;
imageBox1.Image = _result.Mat;

//Displaying predicted result on screen
// LBPH returns -1 if face is recognized
if ((temp_result != -1) && (ER.Distance < 55)){  
     //I get best accuracy at 55, you should try different values to determine best results
     // Do something with detected image
}

Mevcut kodumu düzenleyebilir ve java'da bunun için çalışan bir örnek verebilir misiniz?
R.Coder
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.