从 OpenCV 距离变换中得到奇怪的结果

Posted

技术标签:

【中文标题】从 OpenCV 距离变换中得到奇怪的结果【英文标题】:Getting odd results from OpenCV distance transform 【发布时间】:2017-02-28 06:54:18 【问题描述】:

我正在尝试使用 openCV 实现类似的功能

https://mathematica.stackexchange.com/questions/19546/image-processing-floor-plan-detecting-rooms-borders-area-and-room-names-t

但是,我遇到了一些问题(可能是由于我自己对使用 OpenCV 的无知)。

当我尝试对我的图像执行距离变换时,我根本没有得到预期的结果。

这是我正在使用的原始图像

这是我使用 opencv 进行一些清理后得到的图像

这是我尝试对上图进行距离变换后得到的奇怪现象。我的理解是,这应该看起来更像是一个模糊的热图。如果我按照 opencv 示例通过了这一点并尝试运行一个阈值来找到距离峰值,我只会得到一个黑色图像。

到目前为止,这是我使用几个不同的 opencv 示例拼凑而成的代码

    cv::Mat outerBox = cv::Mat(matImage.size(), CV_8UC1);
cv::Mat kernel = (cv::Mat_<uchar>(3,3) << 0,1,0,1,1,1,0,1,0);

for(int x = 0; x < 3; x++) 
    cv::GaussianBlur(matImage, matImage, cv::Size(11,11), 0);
    cv::adaptiveThreshold(matImage, outerBox, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 5, 2);
    cv::bitwise_not(outerBox, outerBox);

    cv::dilate(outerBox, outerBox, kernel);
    cv::dilate(outerBox, outerBox, kernel);

    removeBlobs(outerBox, 1);

    erode(outerBox, outerBox, kernel);


cv::Mat dist;
cv::bitwise_not(outerBox, outerBox);
distanceTransform(outerBox, dist, cv::DIST_L2, 5);
// Normalize the distance image for range = 0.0, 1.0
// so we can visualize and threshold it
normalize(dist, dist, 0, 1., cv::NORM_MINMAX);


    //using a threshold at this point like the opencv example shows to find peaks just returns a black image right now
//threshold(dist, dist, .4, 1., CV_THRESH_BINARY);
//cv::Mat kernel1 = cv::Mat::ones(3, 3, CV_8UC1);
//dilate(dist, dist, kernel1);


self.mainImage.image = [UIImage fromCVMat:outerBox];

void removeBlobs(cv::Mat &outerBox, int iterations) 
int count=0;
int max=-1;

cv::Point maxPt;

for(int iteration = 0; iteration < iterations; iteration++) 

    for(int y=0;y<outerBox.size().height;y++)
    
        uchar *row = outerBox.ptr(y);
        for(int x=0;x<outerBox.size().width;x++)
        
            if(row[x]>=128)
            

                int area = floodFill(outerBox, cv::Point(x,y), CV_RGB(0,0,64));

                if(area>max)
                
                    maxPt = cv::Point(x,y);
                    max = area;
                
            
        

    

    floodFill(outerBox, maxPt, CV_RGB(255,255,255));
    for(int y=0;y<outerBox.size().height;y++)
    
        uchar *row = outerBox.ptr(y);
        for(int x=0;x<outerBox.size().width;x++)
        
            if(row[x]==64 && x!=maxPt.x && y!=maxPt.y)
            
                int area = floodFill(outerBox, cv::Point(x,y), CV_RGB(0,0,0));
            
        
    


我已经为此苦苦挣扎了几个小时,但我完全陷入了困境,因此我们将不胜感激。这有点超出我的深度,我觉得我可能只是在某个地方犯了一些基本错误而没有意识到。

编辑: 使用与上面运行 OpenCV for Mac(不是 ios)相同的代码,我得到了预期的结果

这似乎表明问题出在 OpenCV 建议使用的 Mat -> UIImage 桥接上。我将继续使用 Mac 库来测试我的代码,但如果能够从 UIImage 桥接中获得一致的结果肯定会很好。

    + (UIImage*)fromCVMat:(const cv::Mat&)cvMat

    // (1) Construct the correct color space
    CGColorSpaceRef colorSpace;
    if ( cvMat.channels() == 1 ) 
        colorSpace = CGColorSpaceCreateDeviceGray();
     else 
        colorSpace = CGColorSpaceCreateDeviceRGB();
    

    // (2) Create image data reference
    CFDataRef data = CFDataCreate(kCFAllocatorDefault, cvMat.data, (cvMat.elemSize() * cvMat.total()));

    // (3) Create CGImage from cv::Mat container
    CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
    CGImageRef imageRef = CGImageCreate(cvMat.cols,
                                        cvMat.rows,
                                        8,
                                        8 * cvMat.elemSize(),
                                        cvMat.step[0],
                                        colorSpace,
                                        kCGImageAlphaNone | kCGBitmapByteOrderDefault,
                                        provider,
                                        NULL,
                                        false,
                                        kCGRenderingIntentDefault);

    // (4) Create UIImage from CGImage
    UIImage * finalImage = [UIImage imageWithCGImage:imageRef];

    // (5) Release the references
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CFRelease(data);
    CGColorSpaceRelease(colorSpace);

    // (6) Return the UIImage instance
    return finalImage;

【问题讨论】:

在进行距离变换之前,您是否尝试在清理后的平面图中将背景更改为黑色而不是白色? 您问题中的图片已消失。 @m3h0w imgur 似乎有容量问题。图像也为我来来去去。 @RickM。请检查我对问题的更新。似乎 openCV 代码正在运行,但 iOS UIImage 桥接未显示与使用 Mac 库的 imshow 输出一致的图像。 @bojangles 对不起队友,不幸的是我没有 UIImage 桥接的经验。但很高兴知道您的原始代码正在运行!祝你好运! 【参考方案1】:

我在 OpenCV 中使用 python 计算了距离变换,我得到了这个:

您说“我得到的只是黑色图像”。好吧,我最初遇到了同样的问题,直到我将图像转换为类型 int 使用:np.uint8(dist_transform)

我也做了一些额外的事情(你可能/可能不需要它)。为了在一定程度上分割房间,我对距离变换的图像进行了阈值处理。结果我得到了这个:

【讨论】:

感谢您的意见!我启动了 mac 版本的 openCV 并复制了我的代码,当使用 imshow 显示图像时,我得到了非常相似的结果。但是,我仍然会在 iOS 版本上得到奇怪的输出,显示使用 OpenCV 在 Mat 和 UIImage 之间提供的桥接生成的 UIImage。好消息是这意味着我的代码可能一直在工作,坏消息是我可能只需要使用 Mac 版本的库进行测试,这令人沮丧。

以上是关于从 OpenCV 距离变换中得到奇怪的结果的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV距离变换错误

OpenCV直线拟合检测

OpenCV 距离变换中的像素索引

opencv::基于距离变换与分水岭的图像分割

OpenCV(26)图像分割 -- 距离变换与分水岭算法(硬币检测扑克牌检测车道检测)

OpenCV从仿射矩阵得到旋转量平移量缩放量