private static Mat rodr2quat(Mat rodr) { double t = Core.norm(rodr); double[] r = new double[3]; rodr.get(0, 0, r); double[] quat = { Math.cos(t / 2), Math.sin(t / 2) * r[0] / t, Math.sin(t / 2) * r[1] / t, Math.sin(t / 2) * r[2] / t }; Mat quatm = new Mat(4, 1, CvType.CV_64F); quatm.put(0, 0, quat); return quatm; }
/** * Analyze video frames using computer vision approach and generate a ArrayList<AttitudeRec> * * @param recs output ArrayList of AttitudeRec * @return total number of frame of the video */ private int analyzeVideo(ArrayList<AttitudeRec> recs) { VideoMetaInfo meta = new VideoMetaInfo(new File(mPath, "videometa.json")); int decimation = 1; if (meta.fps > DECIMATION_FPS_TARGET) { decimation = (int) (meta.fps / DECIMATION_FPS_TARGET); meta.fps /= decimation; } VideoDecoderForOpenCV videoDecoder = new VideoDecoderForOpenCV( new File(mPath, "video.mp4"), decimation); // every 3 frame process 1 frame Mat frame; Mat gray = new Mat(); int i = -1; Size frameSize = videoDecoder.getSize(); if (frameSize.width != meta.frameWidth || frameSize.height != meta.frameHeight) { // this is very unlikely return -1; } if (TRACE_VIDEO_ANALYSIS) { Debug.startMethodTracing("cvprocess"); } Size patternSize = new Size(4, 11); float fc = (float) (meta.frameWidth / 2.0 / Math.tan(meta.fovWidth / 2.0)); Mat camMat = cameraMatrix(fc, new Size(frameSize.width / 2, frameSize.height / 2)); MatOfDouble coeff = new MatOfDouble(); // dummy MatOfPoint2f centers = new MatOfPoint2f(); MatOfPoint3f grid = asymmetricalCircleGrid(patternSize); Mat rvec = new MatOfFloat(); Mat tvec = new MatOfFloat(); MatOfPoint2f reprojCenters = new MatOfPoint2f(); if (LOCAL_LOGV) { Log.v(TAG, "Camera Mat = \n" + camMat.dump()); } long startTime = System.nanoTime(); while ((frame = videoDecoder.getFrame()) != null) { if (LOCAL_LOGV) { Log.v(TAG, "got a frame " + i); } // has to be in front, as there are cases where execution // will skip the later part of this while i++; // convert to gray manually as by default findCirclesGridDefault uses COLOR_BGR2GRAY Imgproc.cvtColor(frame, gray, Imgproc.COLOR_RGB2GRAY); boolean foundPattern = Calib3d.findCirclesGridDefault( gray, patternSize, centers, Calib3d.CALIB_CB_ASYMMETRIC_GRID); if (!foundPattern) { // skip to next frame continue; } if (OUTPUT_DEBUG_IMAGE) { Calib3d.drawChessboardCorners(frame, patternSize, centers, true); } // figure out the extrinsic parameters using real ground truth 3D points and the pixel // position of blobs found in findCircleGrid, an estimated camera matrix and // no-distortion are assumed. boolean foundSolution = Calib3d.solvePnP(grid, centers, camMat, coeff, rvec, tvec, false, Calib3d.CV_ITERATIVE); if (!foundSolution) { // skip to next frame if (LOCAL_LOGV) { Log.v(TAG, "cannot find pnp solution in frame " + i + ", skipped."); } continue; } // reproject points to for evaluation of result accuracy of solvePnP Calib3d.projectPoints(grid, rvec, tvec, camMat, coeff, reprojCenters); // error is evaluated in norm2, which is real error in pixel distance / sqrt(2) double error = Core.norm(centers, reprojCenters, Core.NORM_L2); if (LOCAL_LOGV) { Log.v(TAG, "Found attitude, re-projection error = " + error); } // if error is reasonable, add it into the results if (error < REPROJECTION_THREASHOLD) { double[] rv = new double[3]; rvec.get(0, 0, rv); recs.add(new AttitudeRec((double) i / meta.fps, rodr2rpy(rv))); } if (OUTPUT_DEBUG_IMAGE) { Calib3d.drawChessboardCorners(frame, patternSize, reprojCenters, true); Highgui.imwrite( Environment.getExternalStorageDirectory().getPath() + "/RVCVRecData/DebugCV/img" + i + ".png", frame); } } if (LOCAL_LOGV) { Log.v(TAG, "Finished decoding"); } if (TRACE_VIDEO_ANALYSIS) { Debug.stopMethodTracing(); } if (LOCAL_LOGV) { // time analysis double totalTime = (System.nanoTime() - startTime) / 1e9; Log.i(TAG, "Total time: " + totalTime + "s, Per frame time: " + totalTime / i); } return i; }
/** * Appelée à chaque nouvelle prise de vue par la caméra. * * <p>Son comportement sera différent suivant ce que l'on cherche à faire : * * <ul> * <li>Si la porte n'est pas stable, on cherche alors à détecter l'événement porte stable pour * pouvoir prendre une photo. * <li>Si la porte est stable mais pas fermée, cela signifie que l'on a déjà pris une photo du * contenu du frigo et on attend que la porte soit fermée pour revenir dans l'état initial. * </ul> * * @param inputFrame Image captée par la caméra */ public Mat onCameraFrame(CvCameraViewFrame inputFrame) { Mat current = inputFrame.rgba(); if (stable && !fermee) { // Une photo a été prise // On va rechercher l'événement : le flux vidéo représente des images noires Scalar scalaireN = new Scalar(0x00, 0x00, 0x00, 0xFF); Mat noir = new Mat(current.size(), current.type(), scalaireN); // noir est une matrice noire // Comparaison avec une image noire, résultat stocké dans une matrice diffNoir Mat diffNoir = new Mat(current.size(), current.type()); Core.absdiff(current, noir, diffNoir); Double normeDiffNoir = new Double(Core.norm(diffNoir)); // Calclule de la norme de cette matrice n.add(normeDiffNoir); // Ajout de cette norme dans un conteneur compteur++; // Compteur du nombre d'images prises if (compteur > 11) { // S'il y a suffisamment d'images déjà prises, on vérifie que la porte est fermée fermee = true; int i = 0; while (fermee && i < 10) { // La porte est fermee si sur les dix dernières photos prises, la différence // entre une image noire et l'image current n'est pas trop grande. if (n.get(compteur - 1 - i) > 4500) { fermee = false; // Si cette différence est trop grande, on considère que la porte n'est pas // fermée } i++; } // Si elle n'a jamais été trop grande, la porte est effectivement fermée if (fermee) { // Remise à 0 du compteur s'il doit être réutilisé pour une nouvelle photo // De même pour le tableau n compteur = 0; n.clear(); finish(); // Retour sur l'activité principale qui attend une ouverture du frigo. } } } else if (!stable) { // Aucune photo n'a encore été prise // On va rechercher l'événement : l'image est stable if (buffer == null) { // Première image reçue, il faut créer une matrice buffer qui contiendra // l'image précédente buffer = new Mat(current.size(), current.type()); buffer = current.clone(); } else { // C'est au moins la deuxième image reçue // Comparaison entre l'image précédente et l'image courante, résultat stocké dans une // matrice diffBuffer Mat diffBuffer = new Mat(current.size(), current.type()); Core.absdiff(current, buffer, diffBuffer); Double normeDiffBuffer = new Double(Core.norm(diffBuffer)); // Calcul de la norme de cette matrice n.add(normeDiffBuffer); // Ajout de cette norme dans un conteneur compteur++; // Compteur du nombre d'images prises if (compteur > 11) { // S'il y a suffisamment d'images déjà prises, on vérifie que la porte est stable stable = true; int i = 0; while (stable && i < 10) { // On est stable si sur les dix dernières prises, la différence entre // l'image current est l'image stockée n'est pas trop grande if (n.get(compteur - 1 - i) > 4500) { stable = false; } i++; } if (stable) { Log.i(TAG, "Prise de la photo"); // Si l'image est stable, il faut vérifier tout d'abord que la porte n'est pas fermée. // (on effectue ici le même traîtement que pour une détection de porte fermée) Scalar scalaireN = new Scalar(0x00, 0x00, 0x00, 0xFF); Mat noir = new Mat(current.size(), current.type(), scalaireN); Mat diffNoir = new Mat(current.size(), current.type()); Core.absdiff(current, noir, diffNoir); Double normeDiffNoir = new Double(Core.norm(diffNoir)); if (normeDiffNoir > 4500) { // Si la porte n'est pas fermée, on va sauvegarder l'image avant de l'envoyer File pictureFileDir = getDir(); SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy-HH.mm.ss"); String date = dateFormat.format(new Date()); String photoFile = "PictureCV_" + date + ".jpg"; // Nom du fichier String filename = pictureFileDir.getPath() + File.separator + photoFile; // On doit convertir les couleurs avant de sauvegarder l'image. // La description de la fonction cvtColor explique pourquoi Imgproc.cvtColor(current, current, Imgproc.COLOR_BGR2RGB); Highgui.imwrite(filename, current); // Sauvegarde Log.i(TAG, "Photo sauvegardée"); // Remise à 0 du compteur s'il doit être réutilisé pour une nouvelle photo // De même pour le tableau n compteur = 0; n.clear(); /* //Tentative de reconnaissance d'image //On va essayer de détecter la présence d'une banane pour chaque nouvelle image //captée par le téléphone Mat Grey = inputFrame.gray(); //Image prise par la caméra MatOfRect bananas = new MatOfRect(); Size minSize = new Size(30,20); Size maxSize = new Size(150,100); Log.i(TAG, "Tentative de détection de banane"); mCascadeClassifier.detectMultiScale(Grey, bananas, 1.1, 0, 10,minSize,maxSize); if (bananas.rows()>0){ Log.i(TAG, "Nombre de bananes détectées : " + bananas.rows()); } envoiPhoto(filename, bananas.rows()); //Envoi de la photo avec les données de reconnaissance //Fin de la reconnaissance de l'image */ envoiPhoto(filename); // Envoi de la photo sans les données de reconnaissance } else { // Cas où a porte est fermée // Remise à 0 du compteur s'il doit être réutilisé pour une nouvelle photo // De même pour le tableau n compteur = 0; n.clear(); finish(); } } } buffer = current.clone(); } } return inputFrame.rgba(); }