Contents
Introduction
Concatenating images aka creating a larger image by placing multiple smaller images next to each other is a common thing to do. In general, this is a straight forward process using OpenCV however, there are a couple of different ways to do and not all are applicable in all cases. When prototyping in Python knowing that the final version is going to be programmed using C++, then makes little sense to use stitching functions that are specific to the Python API.
NB!: Image stitching usually implies creating one picture out of many smaller ones with transformations applied to create an error-free panoramic image.
The fundamental requirement for concatenating images is that they are of the same type and compatible shape. Concatenating a grayscale and a BGR images requires converting the grayscale image to BGR first. The axis of concatenation needs to be of the same size as well.
Concatenating cv::Mat
When concatenating cv::Mat
or cv::UMat
, then there are basically two options:
- the easy one (
cv::hconcat
/cv::vconcat
) - the manual memory allocation and copy approach (
copyTo
)
cv::hconcat
and cv::vconcat
can be used in different ways. If only two cv::Mat
should be concatenated, then cv::hconcat(img_0, img_1, img_result)
does the job. When concatenating multiple images, then an array or vector containing input images for concatenation can be used:
// C++
#include <opencv2/opencv.hpp>
#include <vector>
int main()
{
cv::Size mat_size(100,100);
cv::Mat blue(mat_size, CV_8UC3, cv::Scalar(255,0,0));
cv::Mat green(mat_size, CV_8UC3, cv::Scalar(0,255,0));
cv::Mat red(mat_size, CV_8UC3, cv::Scalar(0,0,255));
cv::Mat h_concat_0, h_concat_1, h_concat_2;
cv::Mat v_concat;
cv::Mat h_v_concat;
std::vector<cv::Mat> imgs_for_concat_0 = {blue, green, red};
std::vector<cv::Mat> imgs_for_concat_1 = {red, blue, green};
std::vector<cv::Mat> imgs_for_concat_2 = {green, red ,blue};
cv::hconcat(imgs_for_concat_0, h_concat_0);
cv::hconcat(imgs_for_concat_1, h_concat_1);
cv::hconcat(imgs_for_concat_2, h_concat_2);
cv::vconcat(imgs_for_concat_0, v_concat);
std::vector<cv::Mat> imgs_for_concat_3 = {h_concat_0, h_concat_1, h_concat_2};
cv::vconcat(imgs_for_concat_3, h_v_concat);
}
The python code looks identical. Both tuple
and list
can be used:
# Python
import cv2
# ...
h_concat = cv2.hconcat([blue,green,red])
v_concat = cv2.vconcat((blue,green,red))
cv::hconcat
concatenates images horizontally:

cv::vconcat
concatenates images vertically:

And of course they can be combined:

The second approach is a bit more fundamental. A new cv::Mat
of desired output size could be pre-allocated and all single images are copied into their correct position in the output image using copyTo
.
cv::Size mat_size(100,100);
cv::Size h_concat_size(300,100);
cv::Size v_concat_size(100,300);
cv::Mat blue(mat_size, CV_8UC3, cv::Scalar(255,0,0));
cv::Mat green(mat_size, CV_8UC3, cv::Scalar(0,255,0));
cv::Mat red(mat_size, CV_8UC3, cv::Scalar(0,0,255));
std::vector<cv::Mat> imgs_for_concat_0 = {blue, green, red};
// initialize new mats
cv::Mat h_concat(h_concat_size, CV_8UC3);
cv::Mat v_concat(v_concat_size, CV_8UC3);
int start_col = 0;
int start_row = 0;
int curr_cols;
int curr_rows;
// hconcat
for (size_t i=0; i < imgs_for_concat_0.size(); ++i)
{
curr_cols = imgs_for_concat_0[i].cols;
curr_rows = imgs_for_concat_0[i].rows;
imgs_for_concat_0[i].copyTo(h_concat(cv::Rect(start_col, start_row, curr_cols, curr_rows)));
start_col += curr_cols;
}
// vconcat
start_col = 0;
start_row = 0;
for (size_t i=0; i < imgs_for_concat_0.size(); ++i)
{
curr_cols = imgs_for_concat_0[i].cols;
curr_rows = imgs_for_concat_0[i].rows;
imgs_for_concat_0[i].copyTo(v_concat_size(cv::Rect(start_col, start_row, curr_cols, curr_rows)));
start_row += curr_rows;
}
In theory this approach has the advantage that images of incompatible shapes do not have to be reshaped first. That however could be considered bad practice ;).
Concatenating cv::cuda::GpuMat
There exists no such equivalent to cv::hconcat
/cv::vconcat
for the cv::cuda
namespace/capabilities. Therefore, we are limited to the.copyTo
approach which works exactly as shown above just with cv::cuda::GpuMat
instead of cv::cuda::Mat
.
Concatenating np.array
When using the OpenCV’s Python API, then images (cv::Mat
) are NumPy arrays (np.ndarray
). Besides the options above, NumPy offers three options:
np.hstack((img_0,img1))
ornp.concatenate((img_0,img1), axis=1)
np.vstack((img_0,img1))
ornp.concatenate((img_0,img1), axis=0)