Note: This article only provides a simple implementation method and process of this project, as follows:
- Initialize camera parameters, including exposure time, backlight compensation, etc.
- Initialize the conveyor controller and set the conveyor speed.
- Start capturing images and save each frame.
- For each frame, it is registered with the previous frame (eg using SIFT algorithm).
- Based on the registration result, the current frame is transformed, and it is seamlessly stitched with the previous frame.
- Save the stitched image as the final output.
There are similar stitching methods in opencv, such as panoramic stitching, because panoramic stitching is a stitching method based on feature point matching. If this method is available for simple stitching, let's look at a simple code program
// OpenCV库
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char** argv) {
// 初始化相机参数
int exposure_time = 100; // 曝光时间(以毫秒为单位)
int gain = 0;
bool backlight_compensation = true;
// 初始化传送带控制器
double conveyor_speed = 0.5; // 传送带速度(以米/秒为单位)
// 定义图像存储数组
const int num_frames = 100;
Mat frames[num_frames];
// 初始化相机
VideoCapture cap(0);
cap.set(CV_CAP_PROP_FRAME_WIDTH, 640);
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
cap.set(CV_CAP_PROP_EXPOSURE, exposure_time);
cap.set(CV_CAP_PROP_GAIN, gain);
cap.set(CV_CAP_PROP_BACKLIGHT, backlight_compensation);
// 等待相机初始化完成
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
// 开始捕获图像并保存每一帧
for (int i = 0; i < num_frames; ++i) {
cap >> frames[i];
std::string filename = "frame_" + std::to_string(i) + ".jpg";
imwrite(filename, frames[i]);
// 控制传送带运动
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// TODO: 控制传送带运动
// 配准前一帧
if (i > 0) {
// 利用SIFT算法进行特征点匹配和变换估计
Ptr<FeatureDetector> detector = SIFT::create();
std::vector<KeyPoint> keypoints1, keypoints2;
Mat descriptors1, descriptors2;
detector->detectAndCompute(frames[i-1], Mat(), keypoints1, descriptors1);
detector->detectAndCompute(frames[i], Mat(), keypoints2, descriptors2);
// 利用FLANN库进行匹配
FlannBasedMatcher matcher;
std::vector<DMatch> matches;
matcher.match(descriptors1, descriptors2, matches);
// 提取匹配结果中的特征点对
std::vector<Point2f> pts1, pts2;
for (size_t j=0; j<matches.size(); ++j) {
pts1.push_back(keypoints1[matches[j].queryIdx].pt);
pts2.push_back(keypoints2[matches[j].trainIdx].pt);
}
// 利用findHomography函数进行变换估计
Mat H = findHomography(pts1, pts2, RANSAC);
// 对当前帧进行变换
warpPerspective(frames[i], frames[i], H, Size(frames[i].cols,frames[i].rows));
}
// 向左移动前一帧的ROI,然后将当前帧拼接到其右侧
Rect roi(0, 0, i*frames[i].cols, frames[i].rows);
Mat dst = Mat::zeros(frames[i].rows, (i+1)*frames[i].cols, frames[i].type());
frames[i-1](roi).copyTo(dst(Rect(0, 0, i*frames[i].cols, frames[i].rows)));
frames[i].copyTo(dst(Rect(i*frames[i].cols, 0, frames[i].cols, frames[i].rows)));
frames[i] = dst.clone();
}
// 将拼接后的图像保存为最终输出
imwrite("output.jpg", frames[num_frames-1]);
return 0;
}
This program is currently in the research stage, please look forward to the follow-up updates, if you have good suggestions for code friends, welcome to discuss in the comment area, all Chinese people should not see outsiders, make progress together, and make money! !