193 votes

OpenCV C++/Obj-C : Détection d'une feuille de papier / Détection de carrés

J'ai implémenté avec succès l'exemple de détection de carrés d'OpenCV dans mon application de test, mais j'ai maintenant besoin de filtrer la sortie, parce qu'elle est assez désordonnée - ou mon code est-il mauvais ?

Je suis intéressé par les quatre points d'angle du papier pour la réduction de l'obliquité (comme que ) et traitement ultérieur

Entrée et sortie : Input & Output

Image originale :

cliquez sur

Code :

double angle( cv::Point pt1, cv::Point pt2, cv::Point pt0 ) {
    double dx1 = pt1.x - pt0.x;
    double dy1 = pt1.y - pt0.y;
    double dx2 = pt2.x - pt0.x;
    double dy2 = pt2.y - pt0.y;
    return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}

- (std::vector<std::vector<cv::Point> >)findSquaresInImage:(cv::Mat)_image
{
    std::vector<std::vector<cv::Point> > squares;
    cv::Mat pyr, timg, gray0(_image.size(), CV_8U), gray;
    int thresh = 50, N = 11;
    cv::pyrDown(_image, pyr, cv::Size(_image.cols/2, _image.rows/2));
    cv::pyrUp(pyr, timg, _image.size());
    std::vector<std::vector<cv::Point> > contours;
    for( int c = 0; c < 3; c++ ) {
        int ch[] = {c, 0};
        mixChannels(&timg, 1, &gray0, 1, ch, 1);
        for( int l = 0; l < N; l++ ) {
            if( l == 0 ) {
                cv::Canny(gray0, gray, 0, thresh, 5);
                cv::dilate(gray, gray, cv::Mat(), cv::Point(-1,-1));
            }
            else {
                gray = gray0 >= (l+1)*255/N;
            }
            cv::findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
            std::vector<cv::Point> approx;
            for( size_t i = 0; i < contours.size(); i++ )
            {
                cv::approxPolyDP(cv::Mat(contours[i]), approx, arcLength(cv::Mat(contours[i]), true)*0.02, true);
                if( approx.size() == 4 && fabs(contourArea(cv::Mat(approx))) > 1000 && cv::isContourConvex(cv::Mat(approx))) {
                    double maxCosine = 0;

                    for( int j = 2; j < 5; j++ )
                    {
                        double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
                        maxCosine = MAX(maxCosine, cosine);
                    }

                    if( maxCosine < 0.3 ) {
                        squares.push_back(approx);
                    }
                }
            }
        }
    }
    return squares;
}

MODIFIER LE 17/08/2012 :

Pour dessiner les carrés détectés sur l'image, utilisez ce code :

cv::Mat debugSquares( std::vector<std::vector<cv::Point> > squares, cv::Mat image )
{
    for ( int i = 0; i< squares.size(); i++ ) {
        // draw contour
        cv::drawContours(image, squares, i, cv::Scalar(255,0,0), 1, 8, std::vector<cv::Vec4i>(), 0, cv::Point());

        // draw bounding rect
        cv::Rect rect = boundingRect(cv::Mat(squares[i]));
        cv::rectangle(image, rect.tl(), rect.br(), cv::Scalar(0,255,0), 2, 8, 0);

        // draw rotated rect
        cv::RotatedRect minRect = minAreaRect(cv::Mat(squares[i]));
        cv::Point2f rect_points[4];
        minRect.points( rect_points );
        for ( int j = 0; j < 4; j++ ) {
            cv::line( image, rect_points[j], rect_points[(j+1)%4], cv::Scalar(0,0,255), 1, 8 ); // blue
        }
    }

    return image;
}

1 votes

1 votes

Je pense que vous pouvez ajuster le titre de la question pour quelque chose comme Détection d'une feuille de papier si vous pensez que c'est plus approprié.

1 votes

@moosgummi Je cherche à avoir la même fonctionnalité que vous avez implémentée, c'est-à-dire "Détecter les coins de l'image ou du document capturé". Serais-je capable d'utiliser OpenCV dans mon application iPhone ? Veuillez me suggérer une meilleure façon de le faire.

-1voto

Anubhav Rohatgi Points 448

Détecter une feuille de papier, c'est un peu de la vieille école. Si vous voulez vous attaquer à la détection de l'inclinaison, il est préférable de viser d'emblée la détection des lignes de texte. Vous obtiendrez ainsi les extrêmes gauche, droite, haut et bas. Supprimez tous les graphiques de l'image si vous ne le souhaitez pas et faites des statistiques sur les segments de ligne de texte pour trouver la plage d'angle la plus fréquente ou plutôt l'angle. C'est ainsi que vous pourrez trouver un bon angle d'inclinaison. Après cela, vous mettez ces paramètres, l'angle d'inclinaison et les extrêmes, pour redresser et découper l'image selon ce qui est nécessaire.

En ce qui concerne l'exigence actuelle en matière d'image, il est préférable d'essayer CV_RETR_EXTERNAL au lieu de CV_RETR_LIST.

Une autre méthode de détection des bords consiste à entraîner un classificateur de forêts aléatoires sur les bords du papier, puis à utiliser ce classificateur pour obtenir la carte des bords. Cette méthode est de loin la plus robuste, mais elle nécessite une formation et du temps.

Les forêts aléatoires fonctionnent avec des scénarios de différence à faible contraste, par exemple une feuille blanche sur un fond à peu près blanc.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X