2011-08-17 3 views
0

J'essaye de convertir un GDIPlus :: Bitmap en un objet openCV Mat, mais je rencontre des problèmes avec les violations d'accès, ce qui signifie que je '' Je ne fais pas quelque chose de bien, mais j'ai regardé le code encore et encore, et je pense que ça devrait marcher.Conversion de GDIPlus :: Bitmap en cv :: Mat (interface OpenCV C++)

Est-ce que quelqu'un voit un problème évident?

cv::Mat ConvertToOpenCV(Gdiplus::Bitmap &image) { 
    cv::Mat *retval = new cv::Mat(
     image.GetWidth(), image.GetHeight(), CV_8UC3 
    ); 

    Gdiplus::BitmapData source; 

    Gdiplus::Rect rect(0, 0, image.GetWidth(), image.GetHeight()); 
    Gdiplus::Status status = 
     image.LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat24bppRGB, &source); 
    if (status != Gdiplus::Ok) { 
     // Some error condition 
     return retval; // No image copied 
    } 

    BYTE *destination = (BYTE *)retval->data; 

    for (int y = 0; y != source.Height; ++y) { 
     BYTE *src = (BYTE *) source.Scan0 + y * source.Stride; 
     BYTE *dst = (BYTE *)(destination + y * retval->step); 
     memcpy(dst, src, 3 * source.Width); // Access Violation happens here 
    } 

    image.UnlockBits(&source); 

    return retval; 
} 

Répondre

2

est ici un problème:

cv::Mat *retval = new cv::Mat(
    image.GetWidth(), image.GetHeight(), CV_8UC3 
); 

premier argument de Le constructeur de Mat est des lignes, des colonnes seconde est. Donc vous devriez faire ceci:

cv::Mat *retval = new cv::Mat(
    image.GetHeight(), image.GetWidth(), CV_8UC3 
); 

Cela pourrait causer une violation d'accès.

Modifier

, images OpenCV sont également par défaut BGR, et non RGB. Donc, si vous obtenez ce travail et ensuite afficher l'image avec imshow, vos valeurs bleues et rouges seront en arrière. Vous pouvez résoudre ce problème avec l'appel cv::cvtColor(retval, retval, CV_RGB2BGR) avant votre déclaration de retour.

+0

Doh! Merci beaucoup d'avoir attrapé ça, je n'ai même pas pensé à regarder ça! Et c'est un bon cas de test que je dois ajouter à mes tests unitaires. Je crois que toutes mes images de test sont carrées, et cela cacherait ce comportement! La couleur ne compte pas vraiment pour moi, parce que je convertis en niveaux de gris et en différant les images, donc je n'ai pas pris la peine de le faire. Merci beaucoup d'avoir attrapé ma stupide erreur! – RussTheAerialist

+0

Je dois l'avoir trouvé parce que je suis tellement habitué à trouver des trucs comme ça dans mon propre code! Je ne peux pas vous dire combien de fois j'ai utilisé MIN() alors que j'aurais dû utiliser MAX(). :-) – SSteve

0

Comme SSteve note les constructeurs de tapis vont lignes puis colonnes , utilisez donc la hauteur puis la largeur. Cependant, il n'est pas nécessaire de faire la copie elle-même. Vous pouvez utiliser l'un des constructeurs Mat qui va envelopper les données existantes sans les copier, puis les forcer à copier en appelant la fonction de membre clone.

Le seul autre problème est que Gdiplus :: Bitmap supporte en théorie des charges de configurations de pixels; Cependant, la plupart d'entre eux sont plutôt exotiques. Vous pouvez gérer le cas simple comme suit:

cv::Mat GdiPlusBitmapToOpenCvMat(Gdiplus::Bitmap* bmp) 
{ 
    auto format = bmp->GetPixelFormat(); 
    if (format != PixelFormat24bppRGB) 
     return cv::Mat(); 

    int wd = bmp->GetWidth(); 
    int hgt = bmp->GetHeight(); 
    Gdiplus::Rect rcLock(0, 0, wd, hgt); 
    Gdiplus::BitmapData bmpData; 

    if (!bmp->LockBits(&rcLock, Gdiplus::ImageLockModeRead, format, &bmpData) == Gdiplus::Ok) 
     return cv::Mat(); 

    cv::Mat mat = cv::Mat(hgt, wd, CV_8UC3, static_cast<unsigned char*>(bmpData.Scan0), bmpData.Stride).clone(); 

    bmp->UnlockBits(&bmpData); 
    return mat; 
}