2

目标和问题

我正在尝试使用 OpenCV 3.4.1 动态处理视频文件,方法是抓取每一帧,转换为灰度,然后对其进行 Canny 边缘检测。为了显示图像(也可以即时显示),我创建了一个带有 3 个额外标题的 Mat 类,其宽度是原始帧的三倍。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);
    }
}

问题

  • 为什么我不能在单个窗口中显示整个合成图像,而单独显示它们是可以的?
  • 这些显示器有什么区别?数据显然在那里,如单独的窗口所证明的那样。
  • 为什么只有原始框架行为异常?
4

2 回答 2

1

您的 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
于 2019-01-18T09:59:54.263 回答
0

这是几个问题的组合。

第一个问题是您将类型设置为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);

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

要纠正这个问题,Mat请对灰度数据使用临时 s,并使用cvtColor将其转回 BGR 以写入 ROI。


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

compOrigPart = frame;

这是一个浅拷贝,这意味着它只会compOrigPart再次引用frame(因此它将不再是 的 ROI compositeFrame)。

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


最后,即使您尝试灵活处理输入视频的类型(vid.get(cv::CAP_PROP_FORMAT)

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


把这一切放在一起:

#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);
    }
}

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

示例复合框架

于 2019-01-19T14:08:48.820 回答