Buka CV Face Recognition tidak akurat

13

Dalam aplikasi saya, saya mencoba melakukan pengenalan wajah pada gambar tertentu menggunakan Open CV, di sini pertama-tama saya melatih satu gambar dan kemudian setelah melatih gambar itu jika saya menjalankan pengenalan wajah pada gambar itu berhasil mengenali wajah yang terlatih itu. Namun, ketika saya beralih ke gambar lain dari pengakuan orang yang sama tidak berhasil. Ini hanya berfungsi pada gambar yang terlatih, jadi pertanyaan saya adalah bagaimana cara memperbaikinya?

Pembaruan: Yang ingin saya lakukan adalah bahwa pengguna harus memilih gambar seseorang dari penyimpanan dan kemudian setelah melatih gambar yang dipilih saya ingin mengambil semua gambar dari penyimpanan yang cocok dengan wajah gambar saya yang terlatih.

Ini kelas aktivitas saya:

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

Kelas File Utils Saya:

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

Ini adalah gambar-gambar yang saya coba bandingkan di sini wajah orangnya sama masih dalam pengakuan itu tidak cocok! Gambar 1 Gambar 2

R.Coder
sumber
Ketika saya membangun tugas tahun terakhir saya untuk Sistem Kehadiran Otomatis, saya menggunakan 8-10 gambar diri saya dengan pose dan kondisi pencahayaan yang sedikit berbeda untuk melatih pengklasifikasi.
ZdaR
Anda dapat membalik mat gambar latihan Anda secara horizontal untuk menangani persyaratan itu.
nfl-x
@ nfl-x membalik gambar tidak akan menyelesaikan masalah keakuratan. Kami membutuhkan sesuatu yang lebih baik. Jawaban baru pada tensorflow tampaknya oke tapi tidak ada informasi atau tutorial yang cukup tentang penerapannya untuk android, jadi tebakan terbaik kami adalah tetap memberikan suara pada posting ini. sedemikian rupa sehingga seorang ahli dapat melakukan intervensi dan memberikan solusi yang tepat untuk android
Mr. Patel

Jawaban:

5

Memperbarui

Menurut hasil edit baru dalam pertanyaan, Anda perlu cara untuk mengidentifikasi orang-orang baru dengan cepat yang fotonya mungkin belum tersedia selama fase pelatihan model. Tugas-tugas ini disebut beberapa pembelajaran tembakan . Ini mirip dengan persyaratan badan intelijen / polisi untuk menemukan target mereka menggunakan rekaman kamera CCTV. Seperti biasanya tidak ada cukup gambar dari target tertentu, selama pelatihan, mereka menggunakan model seperti FaceNet . Saya benar-benar menyarankan membaca koran, namun, saya menjelaskan beberapa hal penting di sini:

  • Secara umum, lapisan terakhir dari classifier adalah vektor * 1 dengan n-1 dari elemen hampir sama dengan nol, dan satu dekat dengan 1. Elemen dekat dengan 1, menentukan prediksi classifier tentang label input. Arsitektur CNN yang khas
  • Para penulis menemukan bahwa jika mereka melatih jaringan classifier dengan fungsi kerugian spesifik pada dataset wajah yang besar, Anda dapat menggunakan output lapisan semi final sebagai representasi wajah apa pun, terlepas dari itu berada di set pelatihan atau tidak, penulis menyebutnya vektor Embedding Wajah .
  • Hasil sebelumnya berarti bahwa dengan model FaceNet yang sangat terlatih, Anda dapat meringkas wajah apa pun menjadi vektor. Atribut yang sangat menarik dari pendekatan ini adalah bahwa vektor-vektor wajah orang tertentu dalam sudut / posisi / keadaan yang berbeda memiliki proksimat dalam ruang euclidian (properti ini ditegakkan oleh fungsi kehilangan yang dipilih penulis).masukkan deskripsi gambar di sini
  • Singkatnya, Anda memiliki model yang mendapat wajah sebagai input dan mengembalikan vektor. Vektor yang berdekatan satu sama lain sangat mungkin milik orang yang sama (Untuk memeriksa apakah Anda dapat menggunakan KNN atau hanya jarak euclidian sederhana).

Salah satu implementasi dari FaceNet dapat ditemukan di sini . Saya sarankan Anda mencoba menjalankannya di komputer Anda untuk mengetahui apa yang sebenarnya Anda hadapi. Setelah itu, mungkin yang terbaik adalah melakukan hal berikut:

  1. Ubah model FaceNet yang disebutkan dalam repositori ke versi tflite-nya ( blogpost ini mungkin membantu)
  2. Untuk setiap foto yang dikirimkan oleh pengguna, gunakan API Wajah untuk mengekstrak wajah
  3. Gunakan model minified di aplikasi Anda untuk mendapatkan embeddings wajah dari wajah yang diekstraksi.
  4. Proses semua gambar di galeri pengguna, dapatkan vektor untuk wajah-wajah di foto.
  5. Kemudian bandingkan setiap vektor yang ditemukan di step4 dengan masing-masing vektor yang ditemukan di step3 untuk mendapatkan kecocokan.

Jawaban Asli

Anda menemukan salah satu tantangan pembelajaran mesin yang paling umum: Overfitting. Deteksi wajah dan pengenalan merupakan bidang penelitian besar dan hampir semua model yang cukup akurat menggunakan semacam pembelajaran mendalam. Perhatikan bahwa mendeteksi wajah secara akurat tidak semudah kelihatannya, namun saat Anda melakukannya di android, Anda dapat menggunakan API Wajah untuk tugas ini. (Teknik lain yang lebih canggih seperti MTCNN terlalu lambat / sulit untuk digunakan pada handset). Telah ditunjukkan bahwa hanya memberi makan model dengan foto wajah dengan banyak suara latar belakang atau beberapa orang di dalamnya tidak berfungsi. Jadi, Anda benar-benar tidak dapat melewati langkah ini.

Setelah mendapatkan wajah terpangkas yang bagus dari target kandidat dari latar belakang, Anda harus mengatasi tantangan mengenali wajah yang terdeteksi. Sekali lagi, semua model yang kompeten untuk yang terbaik dari pengetahuan saya, menggunakan semacam pembelajaran mendalam / jaringan saraf convolutional. Menggunakannya di ponsel adalah sebuah tantangan, tetapi berkat Tensorflow Lite Anda dapat memperkecil dan menjalankannya di dalam aplikasi Anda. Sebuah proyek tentang pengenalan wajah pada ponsel android yang telah saya kerjakan ada di sini yang dapat Anda periksa. Perlu diingat bahwa setiap model yang baik harus dilatih pada banyak contoh data berlabel, namun ada sejumlah besar model yang sudah dilatih pada kumpulan data besar wajah atau tugas pengenalan gambar lainnya, untuk mengubah mereka dan menggunakan pengetahuan mereka yang ada, kita dapat menggunakantransfer belajar , untuk memulai cepat pada deteksi objek dan belajar transfer yang terkait erat dengan kasus Anda, periksa posting blog ini .

Secara keseluruhan, Anda harus mendapatkan banyak contoh wajah yang ingin Anda deteksi plus banyak foto wajah orang yang tidak Anda pedulikan, maka Anda perlu melatih model berdasarkan sumber daya yang disebutkan di atas, dan kemudian Anda perlu gunakan TensorFlow lite untuk mengurangi ukurannya dan menyematkannya ke dalam aplikasi Anda. Untuk setiap frame, Anda memanggil API Wajah Android dan memasukkan (wajah yang mungkin terdeteksi) ke dalam model dan mengidentifikasi orang tersebut.

Bergantung pada tingkat toleransi Anda terhadap keterlambatan dan jumlah ukuran pelatihan yang ditetapkan dan jumlah target, Anda bisa mendapatkan berbagai hasil, namun, akurasi% 90 + mudah dicapai jika Anda hanya memiliki beberapa orang target.

Farzad Vertigo
sumber
Saya tidak ingin menggunakan koneksi jaringan di aplikasi saya sehingga google cloud vision tidak dipertanyakan tetapi tensor flow lite tampaknya cukup menarik, gratis? dan jika Anda dapat memberikan contohnya, saya akan sangat menghargainya! Terima kasih
R.Coder
Omong-omong jawaban yang bagus!
R.Coder
Gratis. Lihat ini untuk contoh yang berfungsi. Kami dapat mengidentifikasi wajah 225 orang tanpa menggunakan koneksi jaringan dengan akurasi sangat tinggi meskipun ada beberapa gangguan di sisi pengalaman pengguna. Tapi itu harus menjadi awal yang baik.
Farzad Vertigo
Oke saya akan mencobanya
R.Coder
1
Berhasil !!!! Saya akhirnya mengekstrak tflite model wajah bersih itu dan mendapatkan akurasi di atas 80% pada satu gambar yang terlatih. tetapi kompleksitas waktu benar-benar sangat besar !!, Untuk membandingkan dua gambar dibutuhkan minimal 5 hingga 6 detik setiap ide tentang cara menguranginya?
R.Coder
2

Jika saya mengerti dengan benar, Anda melatih penggolong dengan satu gambar. Dalam hal ini, gambar khusus ini adalah segalanya yang dapat dikenali oleh pengklasifikasi. Anda akan membutuhkan set gambar pelatihan yang jauh lebih besar yang menunjukkan orang yang sama, setidaknya 5 atau 10 gambar berbeda.

Florian Echtler
sumber
Apakah Anda memiliki contoh tentang cara melakukannya?
R.Coder
Ya saya sedang melakukan pengenalan wajah pada gambar statis tunggal
R.Coder
Lihat di sini untuk contoh bagaimana menggunakan train(): docs.opencv.org/3.4/dd/d65/…
Florian Echtler
Jawaban ini tidak membantu jika Anda dapat memberikan beberapa contoh kode yang berkaitan dengan android akan lebih baik!
R.Coder
0

1) Ubah nilai ambang sambil menginisialisasi LBPHrecognizer ke -> LBPHFaceRecognizer (1, 8, 8, 8, 100)

2) latih setiap wajah dengan minimal 2-3 gambar karena pengenal ini terutama bekerja pada perbandingan

3) Tetapkan ambang batas akurasi saat mengenali. Lakukan sesuatu seperti ini:

//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
}
Riz
sumber
Bisakah Anda mengedit kode saya saat ini dan memberikan contoh yang berfungsi untuk melakukannya di java?
R.Coder