열린 CV 얼굴 인식이 정확하지 않습니다


13

내 응용 프로그램에서 Open CV를 사용하여 특정 이미지에서 얼굴 인식을 시도하고 있습니다. 여기서 먼저 하나의 이미지를 훈련 한 다음 해당 이미지에서 얼굴 인식을 실행하면 해당 이미지를 훈련 한 후 훈련 된 얼굴을 성공적으로 인식합니다. 그러나 같은 사람 인식의 다른 그림을 볼 때 작동하지 않습니다. 훈련 된 이미지에서만 작동하므로 내 질문은 어떻게 수정합니까?

업데이트 : 내가하고 싶은 일은 사용자가 저장소에서 사람의 이미지를 선택한 다음 선택한 이미지를 훈련 한 후 훈련 된 이미지의 얼굴과 일치하는 저장소에서 모든 이미지를 가져오고 싶습니다.

내 활동 수업은 다음과 같습니다.

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

내 파일 활용 수업 :

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

이것들은 내가 비교하려고하는 이미지입니다. 사람의 얼굴은 여전히 ​​일치하지 않습니다. 이미지 1 이미지 2


나는 자동 출석 시스템에 대한 마지막 학년 과제를 만들 때 약간 다른 포즈와 조명 조건을 가진 8-10 개의 이미지를 사용하여 분류자를 훈련했습니다.
ZdaR

해당 요구 사항을 처리하기 위해 교육 이미지 매트를 가로로 뒤집을 수 있습니다.
nfl-x

@ nfl-x 뒤집기 이미지는 정확성 문제를 해결하지 못합니다 .tensorflow에 대한 더 나은 최근 답변이 필요하지만 안드로이드에 대한 구현에 대한 충분한 정보 또는 자습서가 충분하지 않으므로이 게시물에 계속 투표하는 것이 좋습니다. 전문가가 안드로이드를위한 적절한 솔루션을 제공하고 제공 할 수 있도록
Mr. Patel

답변:


5

최신 정보

질문의 새로운 편집에 따르면 모델의 훈련 단계에서 사진을 사용할 수 없었던 새로운 사람들을 즉시 식별 할 수있는 방법이 필요합니다. 이러한 작업을 소수의 학습 이라고 합니다. 이것은 CCTV 카메라 영상을 사용하여 목표를 찾기위한 정보 / 경찰 기관의 요구 사항과 유사합니다. 일반적으로 특정 대상의 이미지가 충분하지 않으므로 훈련 중에 FaceNet 과 같은 모델을 사용합니다 . 논문을 읽는 것이 좋지만 여기에 몇 가지 주요 내용을 설명합니다.

  • 일반적으로 분류기의 마지막 레이어는 n-1의 요소가 0과 거의 같고 1에 가까운 1 * 1 벡터입니다. 1에 가까운 요소는 입력 레이블에 대한 분류기의 예측을 결정합니다. 일반적인 CNN 아키텍처
  • 저자는 거대한 얼굴 데이터 세트에서 특정 손실 함수를 사용하여 분류 자 ​​네트워크를 학습하는 경우 훈련 세트에 있든 없든 상관없이 준결승 레이어 출력을 얼굴의 표현으로 사용할 수 있다는 것을 알았습니다. 저자는이 벡터를 Face Embedding 이라고 부릅니다 .
  • 이전 결과는 잘 훈련 된 FaceNet 모델을 사용하여 모든 얼굴을 벡터로 요약 할 수 있음을 의미합니다. 이 접근법의 매우 흥미로운 속성은 다른 각도 / 위치 / 상태의 특정 사람 얼굴의 벡터가 유클리드 공간에서 근접해 있다는 것입니다 (이 속성은 저자가 선택한 손실 함수에 의해 시행됩니다).여기에 이미지 설명을 입력하십시오
  • 요약하면,면을 입력으로 받고 벡터를 반환하는 모델이 있습니다. 서로 가까이있는 벡터는 같은 사람에 속할 가능성이 높습니다 (KNN 또는 간단한 유클리드 거리를 사용할 수 있는지 확인하기 위해).

FaceNet의 한 구현은 여기 에서 찾을 수 있습니다 . 실제로 다루는 내용을 알기 위해 컴퓨터에서 실행 해 보는 것이 좋습니다. 그 후에는 다음을 수행하는 것이 가장 좋습니다.

  1. 저장소에 언급 된 FaceNet 모델을 tflite 버전으로 변환하십시오 ( 블로그 포스트가 도움 될 수 있음)
  2. 사용자가 제출 한 각 사진에 대해 Face API를 사용하여 얼굴을 추출하십시오.
  3. 추출 된 얼굴의 얼굴 임베딩을 얻으려면 앱에서 축소 된 모델을 사용하십시오.
  4. 사용자 갤러리의 모든 이미지를 처리하여 사진에서 얼굴의 벡터를 가져옵니다.
  5. 그런 다음 4 단계에서 찾은 각 벡터와 3 단계에서 찾은 각 벡터를 비교하여 일치하는 것을 얻습니다.

원래 답변

머신 러닝의 가장 큰 과제 중 하나 인 과적 합을 발견했습니다. 얼굴 인식 및 인식은 자체적으로 거대한 연구 분야이며 거의 모든 합리적으로 정확한 모델은 일종의 딥 러닝을 사용합니다. 얼굴을 정확하게 감지하는 것만 큼 쉽지는 않지만 Android에서 수행하는 것처럼 이 작업에 Face API 를 사용할 수 있습니다 . ( MTCNN 과 같은 다른 고급 기술 은 핸드셋에 배포하기에는 너무 느리고 어렵습니다). 배경 소음이 많거나 내부에 여러 사람이있는 얼굴 사진으로 모델을 공급하는 것만으로는 작동하지 않는 것으로 나타났습니다. 따라서이 단계를 건너 뛸 수 없습니다.

배경에서 후보 대상의 멋진 손질 된 얼굴을 얻은 후에는 감지 된 얼굴을 인식하는 문제를 극복해야합니다. 다시 말하지만, 내가 아는 한 모든 유능한 모델은 일종의 딥 러닝 / 컨볼 루션 신경망을 사용하고 있습니다. 휴대폰에서 사용하는 것은 어려운 일이지만 Tensorflow Lite 덕분에 앱을 축소하고 앱 내에서 실행할 수 있습니다. 내가 작업 한 안드로이드 폰의 얼굴 인식에 관한 프로젝트가 있습니다 . 좋은 모델은 레이블이 지정된 여러 데이터 인스턴스에 대해 훈련해야하지만, 대규모 얼굴 데이터 세트 또는 기타 이미지 인식 작업에 대해 이미 훈련 된 수많은 모델이 있으며,이를 조정하고 기존 지식을 사용하려면전송 학습 , 물체 감지 및 확인 밀접하게 사건에 관련되어 전송 학습에 대한 빠른 시작을 위해 블로그 게시물을.

전반적으로 감지하려는 얼굴의 수많은 인스턴스와 관심이없는 사람들의 수많은 얼굴 사진을 가져와야하며 위에서 언급 한 리소스를 기반으로 모델을 훈련해야합니다. TensorFlow Lite를 사용하여 크기를 줄이고 앱에 포함하십시오. 그런 다음 각 프레임에 대해 Android Face API를 호출하고 (아마도 감지 된 얼굴)을 모델에 공급하고 사람을 식별합니다.

지연 허용 오차 수준과 훈련 세트 크기 및 목표 수에 따라 다양한 결과를 얻을 수 있지만 목표 인원이 적은 경우 % 90 + 정확도를 쉽게 달성 할 수 있습니다.


내 앱에서 네트워크 연결을 사용하고 싶지 않으므로 Google 클라우드 비전에 의문의 여지가 없지만 텐서 흐름 라이트는 무료입니까? 그리고 당신이 그것의 작동 예제를 제공 할 수 있다면 나는 그것을 감사하겠습니다! 감사합니다
R.Coder

그건 그렇고 좋은 답변입니다!
R.Coder

무료입니다. 확인 작동 예를 들어. 사용자 경험 측면에 약간의 결함이 있었음에도 불구하고 매우 높은 정확도로 네트워크 연결을 사용하지 않고 225 명의 얼굴을 식별 할 수있었습니다. 그러나 그것은 좋은 시작이되어야합니다.
Farzad Vertigo

좋아 나는 그것을 시도 줄거야
R.Coder

1
작동했습니다 !!!! 나는 결국 그 얼굴 그물 모델 tflite를 추출하고 단일 훈련 된 이미지에서 80 % 이상의 정확도를 얻었습니다. 그러나 시간 복잡도는 정말 엄청납니다 !!, 두 이미지를 비교하는 데 5 초에서 6 초가 걸리면 어떻게 줄일 수 있을까요?
R.Coder

2

올바르게 이해하면 단일 이미지로 분류자를 훈련시킵니다. 이 경우이 특정 이미지는 분류자가 인식 할 수있는 모든 것입니다. 같은 사람을 나타내는 눈에 띄게 더 큰 훈련 세트가 필요합니다.


그렇게하는 방법에 대한 예가 있습니까?
R.Coder

예, 저는 하나의 정적 이미지에서 얼굴 인식을하고 있습니다
R.Coder

사용하는 방법의 예를 여기를 참조하십시오 train(): docs.opencv.org/3.4/dd/d65/...
플로리안 Echtler

이 답변은 안드로이드와 관련된 코딩 된 예제를 제공 할 수 있다면 도움이되지 않습니다!
R.Coder

0

1) LBPH 인식기를 초기화하는 동안 임계 값을-> LBPHFaceRecognizer (1, 8, 8, 8, 100)로 변경하십시오.

2) 인식기는 주로 비교 작업을 수행하기 때문에 적어도 2-3 장의 사진으로 각 얼굴을 훈련시킵니다.

3) 인식하는 동안 정확도 임계 값을 설정하십시오. 다음과 같이하십시오 :

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

내 현재 코드를 편집하고 Java로 코드를 작성하는 실제 예제를 제공 할 수 있습니까?
R.Coder
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.