2
votes

Dans quelle mesure les images par seconde calculées peuvent-elles être supérieures aux images par seconde déclarées de l'appareil photo?

J'essaie de mesurer les images par seconde lors du traitement des images de la caméra. Les calculs n'ont rien de spécial et peuvent être trouvés dans cette question: Comment écrire une fonction avec un paramètre dont le type est déduit avec le mot 'auto'? Mon appareil photo est assez ancien et le FPS déclaré par le fabricant n'est pas supérieur à 30 avec une résolution de 640x480. Cependant, lorsque j'exécute ces calculs, cela me montre 40-50 sur les flux en direct. Comment est-ce possible?

Mise à jour: Code:

#include <chrono>
#include <iostream>

using std::cerr;
using std::cout;
using std::endl;

#include <string>
#include <numeric>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/imgproc.hpp>

using cv::waitKey;
using cv::Mat;

using time_type = decltype(std::chrono::high_resolution_clock::now());

void showFPS(Mat* frame, const time_type &startTime);

int main(int argc, char** argv) {

    cv::VideoCapture capture;
    std::string videoDevicePath = "/dev/video0";

    if (!capture.open(videoDevicePath)) {
        std::cerr << "Unable to open video capture.";
        return 1;
    }
    //TODO normally through cmd or from cameraParameters.xml
    bool result;
    result = capture.set(CV_CAP_PROP_FOURCC, CV_FOURCC('M', 'J', 'P', 'G'));
    if (result) {
        std::cout << "Camera: PROP_FOURCC: MJPG option set.";
    } else {
        std::cerr << "Camera: PROP_FOURCC: MJPG option was not set.";
    }
    result = capture.set(CV_CAP_PROP_FRAME_WIDTH, 640);
    if (result) {
        std::cout << "Camera: PROP_FRAME_WIDTH option set.";
    } else {
        std::cerr << "Camera: PROP_FRAME_WIDTH option was not set.";
    }
    result = capture.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
    if (result) {
        std::cout << "Camera: PROP_FRAME_HEIGHT option set.";
    } else {
        std::cerr << "Camera: PROP_FRAME_HEIGHT option was not set.";
    }
    result = capture.set(CV_CAP_PROP_FPS, 30);
    if (result) {
        std::cout << "Camera: PROP_FPS option set.";
    } else {
        std::cerr << "Camera: PROP_FPS option was not set.";
    }

    Mat frame, raw;
    while (cv::waitKey(5) != 'q') {
        auto start = std::chrono::high_resolution_clock::now();
        capture >> raw;

        if (raw.empty()) {
            return 1;
        }
        if (raw.channels() > 1) {
            cv::cvtColor(raw, frame, CV_BGR2GRAY);
        } else {
            frame = raw;
        }
        showFPS(&raw1, start);
    }
    return 0;
}

void showFPS(Mat* frame, const time_type &startTime) {
    typedef std::chrono::duration<float> fsec_t;

    auto stopTime = std::chrono::high_resolution_clock::now();
    fsec_t duration = stopTime - startTime;

    double sec = duration.count();
    double fps = (1.0 / sec);
    std::stringstream s;
    s << "FPS: " << fps;
    cv::putText(*frame, s.str(), Point2f(20, 20), constants::font,
                constants::fontScale, constants::color::green);
}


1 commentaires

Difficile de répondre sans voir votre code pour lire les cadres de la caméra.


3 Réponses :


3
votes

Le FPS de la caméra est le nombre d'images que la caméra peut fournir par seconde. Cela signifie que la caméra fournit une nouvelle image toutes les 33 ms.

De l'autre côté, ce que vous mesurez n'est pas le FPS. Vous mesurez le temps inverse de la fonction de la nouvelle récupération d'image plus la conversion des couleurs. Et cette durée est de 20 à 25 ms, en fonction de vos résultats.

Ce n'est pas une façon correcte de mesurer le FPS, du moins parce que vous ne pouvez pas garantir la synchronisation de ces deux processus.

Si vous voulez mesurer correctement le FPS, vous pouvez mesurer le temps d'affichage des N dernières images.

Pseudocode:

counter = 0;
start = getTime();
N = 100;

while (true) {
  captureFrame();
  convertColor();
  counter++;

  if (counter == N) {
    fps = N / (getTime() - start);
    printFPS(fps);

    counter = 0; 
    start = getTime();
  }
}


0 commentaires

0
votes

Vous avez un cvtColor entre les deux, donc cela affecte votre temps de calcul car le temps de traitement de cvtColor peut varier dans chaque boucle (probablement à cause des autres processus de Windows ).

Prenons cet exemple:

Vous obtenez la première image avec capture au moment 0, puis effectuez un cvtColor et cela prend par exemple 10 ms, puis vous faites un stopTime à moment 10 ms. 23 ms plus tard (33-10), vous capturez la deuxième image. Mais cette fois, cvtColor prend 5 ms (cela peut arriver) et vous faites le deuxième stopTime au moment 38 (33 + 5), donc le premier tick était au moment 10 et le deuxième tick est au moment 38. Maintenant, votre fps devient

1000 / (38-10) = 35,7


4 commentaires

Alors, comment mesurer exactement le FPS? La façon dont Alexey Petrov a écrit?


La réponse est fausse. Si vous continuez l'exemple en supposant que le temps que cvtColor prend des alternances entre 10 et 5 ms, vous verrez la valeur fps alterner entre 36 (1 / .028) et 26 (1 / 0.038), mais pas les valeurs qui sont systématiquement supérieurs à 30, comme indiqué dans la question.


@UlrichStern Eh bien, il n'a pas dit que ça arrivait constamment, je pensais qu'il y avait des pics!


Je ne pense pas qu'on utiliserait "montre-moi 40-50 [fps] sur les flux en direct" pour décrire les valeurs de fps observées si le problème clé dans le calcul des fps était la variation du temps que prend cvtColor . On peut décrire cela comme, par exemple, "il y a une certaine variation autour de 30 ips."



1
votes

La réponse d'Aleksey Petrov n'est pas mauvaise, mais si la moyenne sur les N dernières images donne des valeurs plus lisses, on peut mesurer la fréquence d'images de manière relativement précise sans faire de moyenne. Voici le code de la question modifié pour faire cela:

    // see question for earlier code
    Mat frame, raw;
    time_type prevTimePoint;   // default-initialized to epoch value
    while (waitKey(1) != 'q') {
        capture >> raw;
        auto timePoint = std::chrono::high_resolution_clock::now();

        if (raw.empty()) {
            return 1;
        }
        if (raw.channels() > 1) {
            cv::cvtColor(raw, frame, CV_BGR2GRAY);
        } else {
            frame = raw;
        }

        showFPS(&frame, prevTimePoint, timePoint);

        cv::imshow("frame", frame);
    }
    return 0;
}

void showFPS(Mat* frame, time_type &prevTimePoint, const time_type &timePoint) {
    if (prevTimePoint.time_since_epoch().count()) {
        std::chrono::duration<float> duration = timePoint - prevTimePoint;
        cv::putText(*frame, "FPS: " + std::to_string(1/duration.count()),
            cv::Point2f(20, 40), 2, 2, cv::Scalar(0,255,0));
    }
    prevTimePoint = timePoint;
}

Notez que cela mesure le point de temps juste après le retour de capture >> raw , qui (sans jouer avec OpenCV ) est le plus proche possible lorsque la caméra a envoyé l'image, et que le temps n'est mesuré qu'une seule fois par boucle et comparé à la mesure précédente, ce qui donne une fréquence d'images actuelle assez précise. Bien sûr, si le traitement prend plus de temps que 1 / (fréquence d'images), la mesure sera désactivée.

La raison pour laquelle le code de la question a donné une fréquence d'images trop élevée était en fait le code entre les deux mesures de temps : le now () dans showFPS () et le now () dans la boucle while . Mon intuition est ce code inclus cv :: imshow () , qui n'est pas dans la question et qui avec cv :: waitKey (5) et cv: : putText () est probablement responsable de la plupart du "temps manquant" dans le calcul de la fréquence d'images (provoquant une fréquence d'images trop élevée).


1 commentaires

à quoi cela ressemblerait-il avec l'appel de fonction?