在OpenCV中显示多张(2,3,4,...)图片在同一个窗口中

21

我想在同一个窗口中显示2、3或更多的图像。

我的问题是如何将第二个、第三个图像放置在主图像的右侧(上方、左侧或上方)。

我想使用OpenCV创建类似于这样的东西。

---------------
|      |      |
|      |      |
---------------
|    |        |
|    |        |
---------------

提前感谢 Jorge

6个回答

24

我最近实现了这个功能,所以想分享一下。 它使用C++ API。代码应该是很容易理解的。

    /**
     * @brief makeCanvas Makes composite image from the given images
     * @param vecMat Vector of Images.
     * @param windowHeight The height of the new composite image to be formed.
     * @param nRows Number of rows of images. (Number of columns will be calculated
     *              depending on the value of total number of images).
     * @return new composite image.
     */
    cv::Mat makeCanvas(std::vector<cv::Mat>& vecMat, int windowHeight, int nRows) {
            int N = vecMat.size();
            nRows  = nRows > N ? N : nRows; 
            int edgeThickness = 10;
            int imagesPerRow = ceil(double(N) / nRows);
            int resizeHeight = floor(2.0 * ((floor(double(windowHeight - edgeThickness) / nRows)) / 2.0)) - edgeThickness;
            int maxRowLength = 0;

            std::vector<int> resizeWidth;
            for (int i = 0; i < N;) {
                    int thisRowLen = 0;
                    for (int k = 0; k < imagesPerRow; k++) {
                            double aspectRatio = double(vecMat[i].cols) / vecMat[i].rows;
                            int temp = int( ceil(resizeHeight * aspectRatio));
                            resizeWidth.push_back(temp);
                            thisRowLen += temp;
                            if (++i == N) break;
                    }
                    if ((thisRowLen + edgeThickness * (imagesPerRow + 1)) > maxRowLength) {
                            maxRowLength = thisRowLen + edgeThickness * (imagesPerRow + 1);
                    }
            }
            int windowWidth = maxRowLength;
            cv::Mat canvasImage(windowHeight, windowWidth, CV_8UC3, Scalar(0, 0, 0));

            for (int k = 0, i = 0; i < nRows; i++) {
                    int y = i * resizeHeight + (i + 1) * edgeThickness;
                    int x_end = edgeThickness;
                    for (int j = 0; j < imagesPerRow && k < N; k++, j++) {
                            int x = x_end;
                            cv::Rect roi(x, y, resizeWidth[k], resizeHeight);
                            cv::Size s = canvasImage(roi).size();
                            // change the number of channels to three
                            cv::Mat target_ROI(s, CV_8UC3);
                            if (vecMat[k].channels() != canvasImage.channels()) {
                                if (vecMat[k].channels() == 1) {
                                    cv::cvtColor(vecMat[k], target_ROI, CV_GRAY2BGR);
                                }
                            } else {             
                                vecMat[k].copyTo(target_ROI);
                            }
                            cv::resize(target_ROI, target_ROI, s);
                            if (target_ROI.type() != canvasImage.type()) {
                                target_ROI.convertTo(target_ROI, canvasImage.type());
                            }
                            target_ROI.copyTo(canvasImage(roi));
                            x_end += resizeWidth[k] + edgeThickness;
                    }
            }
            return canvasImage;
    }

这里是样例输出。 由多个图像组成的复合图像


我们该如何更改代码以显示灰度图像呢?因为我尝试过,但它只适用于彩色图像。 - Maystro
1
@Maystro:我已经修改了代码,支持灰度和彩色图像。 - vinvinod
1
我想这是最好的解决方案。无论如何,我不得不稍微修改一下,因为在彩色图像的情况下,您忘记将vecMat[k]数据复制到target_ROI Mat中。 - Joe Aspara

19

非常好的答案。测试后完美地运行了。再次感谢。 - Jorge Vega Sánchez
3
链接已损坏。这是一个新链接:http://code.opencv.org/projects/opencv/wiki/DisplayManyImages - Duncan Calvert
1
它使用的是C API,这在现今已经非常老派了。 - Stefan
1
相同的链接,仍然无法正常访问。 - user4734394
这里是有关编程的内容,请将以下文本从英语翻译成中文。仅返回已翻译的文本:源代码在此处:https://github.com/opencv/opencv/wiki/DisplayManyImages,此简单解决方案看起来也很有前途:https://dev59.com/V2gu5IYBdhLWcg3wzKAc#11069276 - Mark

10

或者只需使用:

Mat a, Mat b, Mat dst // a,b loaded

cv::hconcat(a, b, dst) // horizontal
cv::vconcat(a, b, dst) // vertical

Mat dst -> | a | b |

或者使用向量(vector)来完成:

std::vector<cv::Mat> matrices = {
            a, b
    };
hconcat(matrices, dst);

7
答案取决于您使用的接口(C或C ++)。通用的工作流程是:
  • 创建一个足够大以容纳组合图像的图像( cv :: Mat 用于C ++, IplImage * 用于C)
  • 将您的图像复制到大图像中
    • C ++:使用 Mat :: Mat(const Mat&m,const Range&rowRange,const Range&colRange)构造函数获取指向原始窗口子图像的 cv :: Mat ,然后使用 copyTo 方法将您的小图像复制到大图像中
    • C:在大图像中设置ROI并将小图像复制到其中
  • 显示您的大图像

好的。谢谢你的想法。在OpenCV中,ROI非常重要。 - Jorge Vega Sánchez

3

尝试以下代码(请注意我的注释):

Mat img = imread("lena.JPG");

CV::Mat chann[3], all; //  creating 

split(img, chann); // split an image into their color channel and n keep them inside

a 3 element array called chann

imshow("ppl", img);

hconcat(chann, 3, all); //  joining the images together in a horizontal manner, the 

array, number of array, and the destination

imshow("B :: G :: R",all);     // this just the little help i could provide 

-2
使用OpenCV附带的GUI相当有限。如果您需要执行任何复杂的操作,建议在Windows上使用GUi框架,例如QT或VC++。

我使用MAC。但现在我只想使用OpenCV框架。我知道将来需要使用外部框架以获得更多选项。感谢您的回答。 - Jorge Vega Sánchez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接