繁体   English   中英

opencv中的cv :: imshow仅显示合成图像的一部分,但是单独显示这些部分是可行的。 为什么?

[英]cv::imshow in opencv is only displaying parts of a composite image, but displaying the parts separately works. Why?

目标与问题

我正在尝试使用OpenCV 3.4.1快速处理视频文件,方法是抓取每一帧,转换为灰度,然后对其进行Canny边缘检测。 为了实时显示图像,我创建了一个Mat类,其中包含3个额外的标题,其宽度是原始帧的三倍。 这3个额外的标题代表了我想在合成中显示的图像,并位于合成的第1,第2和第3水平段上。

但是,在图像处理之后,合成图像的显示不符合预期: 第一段(应在原始帧所在的位置)完全是黑色的 ,而其他段(已处理图像的)则显示得很好。 另一方面,如果我在单独的窗口中逐个显示ROI ,则所有图像看起来都很好。

这些是我试图克服此问题的方法:

  1. 使用.copyTo将数据实际复制到适当的图像段中。 结果是一样的。
  2. 我将Canny图像放到compOrigPart ROI中,并且确实在第一段中显示,因此ROI的定义不是问题。
    • 将合成定义为三通道图像
    • 在循环中将其转换为灰度
    • 将经过处理的图像放入其中
    • 转换回BGR
    • 放入原件。

这次整个复合材料都是黑色的,什么也没显示。

  1. 根据gameon67的建议,我也尝试创建一个namedWindow,但这也无济于事。

码:

int main() {

    cv::VideoCapture vid("./Vid.avi");
    if (!vid.isOpened()) return -1;

    int frameWidth = vid.get(cv::CAP_PROP_FRAME_WIDTH);
    int frameHeight = vid.get(cv::CAP_PROP_FRAME_HEIGHT);
    int frameFormat = vid.get(cv::CAP_PROP_FORMAT);

    cv::Scalar fontColor(250, 250, 250);
    cv::Point textPos(20, 20);

    cv::Mat frame;

    cv::Mat compositeFrame(frameHeight, frameWidth*3, frameFormat);
    cv::Mat compOrigPart(compositeFrame, cv::Range(0, frameHeight), cv::Range(0, frameWidth));
    cv::Mat compBwPart(compositeFrame, cv::Range(0, frameHeight), cv::Range(frameWidth, frameWidth*2));
    cv::Mat compEdgePart(compositeFrame, cv::Range(0, frameHeight), cv::Range(frameWidth*2, frameWidth*3));


    while (vid.read(frame)) {
        if (frame.empty()) break;

        cv::cvtColor(frame, compBwPart, cv::COLOR_BGR2GRAY);
        cv::Canny(compBwPart, compEdgePart, 100, 150);
        compOrigPart = frame;

        cv::putText(compOrigPart, "Original", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);
        cv::putText(compBwPart, "GrayScale", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);
        cv::putText(compEdgePart, "Canny edge detection", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);

        cv::imshow("Composite of Original, BW and Canny frames", compositeFrame);
        cv::imshow("Original", compOrigPart);
        cv::imshow("BW", compBwPart);
        cv::imshow("Canny", compEdgePart);
        cv::waitKey(33);
    }
}

问题

  • 为什么不能在一个窗口中显示整个合成图像,而单独显示它们就可以了?
  • 这些显示之间有什么区别? 数据显然在那里,如单独的窗口所示。
  • 为什么只有原始帧出现异常?

您的compBwPart和compEdgePart是灰度图像,因此Mat类型是CV8UC1-单通道,因此您的CompositeFrame也是灰度的。 如果要将这两个图像与彩色图像组合在一起,则必须先将其转换为BGR,然后填充compOrigPart。

while (vid.read(frame)) {
  if (frame.empty()) break;

  cv::cvtColor(frame, compBwPart, cv::COLOR_BGR2GRAY);
  cv::Canny(compBwPart, compEdgePart, 100, 150);
  cv::cvtColor(compositeFrame, compositeFrame, cv::COLOR_GRAY2BGR);
  frame.copyTo(compositeFrame(cv::Rect(0, 0, frameWidth, frameHeight)));

  cv::putText(compOrigPart, "Original", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor); //the rest  of your code

这是几个问题的组合。

第一个问题是您将compositeFrame的类型设置为vid.get(cv::CAP_PROP_FORMAT)返回的值。 不幸的是,该属性似乎并不完全可靠-打开彩色视频后,我得到了它返回0(表示CV_8UC1 ),然后得到3通道( CV_8UC3 )帧。 由于您要让compositeFrame与输入框架具有相同的类型,因此将无法使用。

要解决此问题,而不是使用这些属性,我会在接收到第一帧(根据其尺寸和类型)后懒于初始化compositeFrame和3个ROI。


下一组问题在于这两个语句:

cv::cvtColor(frame, compBwPart, cv::COLOR_BGR2GRAY);
cv::Canny(compBwPart, compEdgePart, 100, 150);

在这种情况下,假设frame是BGR(因为您要转换),这意味着compositeFrame及其ROI也是BGR。 不幸的是,在两种情况下,您都将灰度图像写入ROI。 这将导致重新分配,并且目标Mat将不再是ROI。

要解决此问题,请对灰度数据使用临时Mat ,并使用cvtColor将其转换回BGR以写入ROI。


类似的问题在于以下语句:

compOrigPart = frame;

这是一个浅拷贝,这意味着它只会让compOrigPart另一参考frame (并因此将不再是一个投资回报率compositeFrame )。

您需要的是使用copyTo的深层副本(请注意,数据类型仍然需要匹配,但是之前已修复)。


最后,即使您尝试灵活vid.get(cv::CAP_PROP_FORMAT)输入视频的类型(通过vid.get(cv::CAP_PROP_FORMAT)判断),其余代码实际上仍假定输入为3通道,如果输入为3通道,则会中断输入不是。

至少应该有一些断言来涵盖这一期望。


全部放在一起:

#include <opencv2/opencv.hpp>

int main()
{
    cv::VideoCapture vid("./Vid.avi");
    if (!vid.isOpened()) return -1;

    cv::Scalar fontColor(250, 250, 250);
    cv::Point textPos(20, 20);

    cv::Mat frame, frame_gray, edges_gray;
    cv::Mat compositeFrame;
    cv::Mat compOrigPart, compBwPart, compEdgePart; // ROIs

    while (vid.read(frame)) {
        if (frame.empty()) break;

        if (compositeFrame.empty()) {
            // The rest of code assumes video to be BGR (i.e. 3 channel)
            CV_Assert(frame.type() == CV_8UC3);
            // Lazy initialize once we have the first frame
            compositeFrame = cv::Mat(frame.rows, frame.cols * 3, frame.type());
            compOrigPart = compositeFrame(cv::Range::all(), cv::Range(0, frame.cols));
            compBwPart = compositeFrame(cv::Range::all(), cv::Range(frame.cols, frame.cols * 2));
            compEdgePart = compositeFrame(cv::Range::all(), cv::Range(frame.cols * 2, frame.cols * 3));
        }

        cv::cvtColor(frame, frame_gray, cv::COLOR_BGR2GRAY);
        cv::Canny(frame_gray, edges_gray, 100, 150);

        // Deep copy data to the ROI
        frame.copyTo(compOrigPart);
        // The ROI is BGR, so we need to convert back
        cv::cvtColor(frame_gray, compBwPart, cv::COLOR_GRAY2BGR);
        cv::cvtColor(edges_gray, compEdgePart, cv::COLOR_GRAY2BGR);

        cv::putText(compOrigPart, "Original", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);
        cv::putText(compBwPart, "GrayScale", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);
        cv::putText(compEdgePart, "Canny edge detection", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);

        cv::imshow("Composite of Original, BW and Canny frames", compositeFrame);
        cv::imshow("Original", compOrigPart);
        cv::imshow("BW", compBwPart);
        cv::imshow("Canny", compEdgePart);
        cv::waitKey(33);
    }
}

复合窗口的屏幕截图(在网络上使用一些随机测试视频):

复合框架示例

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM