C++ OpenCV生成九宫格图像
學更好的別人,
做更好的自己。
——《微卡智享》
本文長度為1959字,預計閱讀5分鐘
前言
這幾個月一直在做Android的東西,OpenCV的Demo基本沒做,正好前兩天也剛下載了VS2022,正好借助新的VS2022做個簡單的OpenCV圖像切割成九宮格的Demo。
實現(xiàn)效果
看上圖的最右邊的,就是切分成9個圖的效果,看過我的《趣玩算法--OpenCV華容道AI自動解題》老朋友應該都知道我要干什么了。沒錯,做這個嘛是為了再做一個拼圖的小游戲,右邊每個圖像上都用PutText打印出了圖像對應的區(qū)域數(shù)字,現(xiàn)在是為了標識作用的。
實現(xiàn)思路
| 1 | 加載圖像后用Resize將圖像縮放成正方形大小 |
| 2 | 按圖像起始位置開始,計算每個截取區(qū)域的圖像大小 |
| 3 | 將截取的區(qū)域存入到Vector的容器中,存放的過程中隨機排序 |
| 4 | 生成一個新的畫布,遍歷容器將每個圖像顯示出來 |
核心代碼講解
微卡智享
01
關(guān)于分割的圖像容器
最開始想使用map的方式,后來覺得不太好,就創(chuàng)建了一個結(jié)構(gòu),就是分割后的圖像原來的序號位置,圖像Mat,還有一個是現(xiàn)在的位置三個屬性。
而生成分割后的圖像容器用了一個SplitMats的函數(shù)來實現(xiàn)。
std::vector<CutMat*> MatSet::SplitMats(cv::Mat& img, int cols, int rows) {std::vector<CutMat*> matvts;if (cols == 0 || rows == 0){std::cout << "行數(shù)和列數(shù)不能為0" << std::endl;return matvts;}matvts.resize(cols * rows);//計算平均分的格數(shù)的width和heightint width = img.cols / cols;int height = img.rows / rows;//生成序號列表std::vector<int> nums = GetVtsPos(cols, rows);//根據(jù)輸入的行和列劃分開矩形for (int row = 0; row < rows; row++) {for (int col = 0; col < cols; col++) {//計算當前矩形的起始X和Y坐標int x = col * width;if (x > 0) x++;int y = row * height;if (y > 0) y++;//計算截取矩形的寬和高,加入控制不能超過源圖像的邊界int rwidth = width;if (x + rwidth > img.cols) rwidth = img.cols - x;int rheight = height;if (y + rheight > img.rows) rheight = img.rows - y;//生成截取的矩形并截取圖像存放到map中cv::Rect rect = cv::Rect(x, y, rwidth, rheight);cv::Mat matrect = img(rect);//截取后的圖像需要判斷是否寬高一致,不一致時縮放為一樣大,用于在一張圖像顯示if (rwidth != width || rheight != height) {cv::resize(matrect, matrect, cv::Size(width, height));}//當前Mat的序號int pos = row * rows + col + 1;CutMat* tmpcurmat = new CutMat(pos, matrect);//隨機指定的新位置tmpcurmat->curPosition = GetRandNum(nums);//根據(jù)隨機排序后的位置插入到容器中matvts[tmpcurmat->curPosition - 1] = tmpcurmat;}}return matvts; }載取的過程中要注意的是,根據(jù)圖像大小平均分割后,最后的矩形長度要判斷是否超出圖像邊緣了,如果超出后,需要長度設(shè)為到圖像邊緣的長度,然后再通過Resize來實現(xiàn)設(shè)置圖像相同大小。
02
關(guān)于圖像打亂順序的解決
前面定義的結(jié)構(gòu)里面,通過生成隨機位置賦值給了curPosition屬性,考慮到顯示出來要按照curPosition屬性順序顯示,傳統(tǒng)的方法就是兩個思路:
使用Map存儲,Key為curPosition,遍歷時查找Key找到對應的Map,時間復雜度為O1,用Map空間換時間。
返回的容器,重新按照curPosition排序,時間復雜度On。
因為我們?nèi)萜髦挥?個,所以用上面兩個基本的速度也可以忽略,不過即然在生成的過程中已經(jīng)賦值隨機數(shù)了,所以當時也直接指定存放位置也可以,完全不需要用上面兩種方案。
通過傳入的行和列數(shù)字,直接設(shè)置容器的個數(shù)。
根據(jù)生成的指定位置,直接修改容器的下標值。
整個項目中新建了一個MatSet的類,繪制和生成圖像都在這里實現(xiàn)的,main.cpp就是加載圖像和外部調(diào)用。
完整代碼
MatSet.h
MatSet.cpp
main.cpp
#pragma once #include <opencv2/opencv.hpp> #include <iostream> #include "MatSet.h"using namespace std; using namespace cv;//定義輪廓區(qū)域 vector<vector<Point>> contours;int main(int argc, char** argv) {try{Mat src = imread("E:/DCIM/test8.jpg");if (src.empty()) {cout << "圖像加載失敗。。。。" << endl;waitKey(0);return -1;}//設(shè)置圖像縮放到500*500Mat tmpsrc;resize(src, tmpsrc, Size(500, 500));imshow("src", src);imshow("tmpsrc", tmpsrc);//獲取圖像分割后的集合vector<CutMat*> vtsmat = MatSet::SplitMats(tmpsrc);//將圖像分割后的集合對應的序號列出來for (int i = 0; i < vtsmat.size(); ++i) {Mat tmpmat = vtsmat[i]->mat;string title = to_string(vtsmat[i]->Position);putText(tmpmat, title, Point(tmpmat.cols/2, tmpmat.rows/2), 2, 2, Scalar(0, 0, 255));}//繪制圖像MatSet::DrawPuzzleMat(vtsmat, contours, true);cv::waitKey(0);return 0;}catch (const std::exception& ex){cout << ex.what() << endl;cv::waitKey(0);return -1;} }整個Demo用的VS2022和OpenCV4.5.4做的,用VS2022的C++里,智能提示感覺和VS2019差不多,并不像我上篇說的和C#中一樣強大。另一個問題就是用了OpenCV4.5.4后,運行過程中控制臺多了一些加載錯誤的輸出,雖然并不影響運行,不過看著不舒服。圖如下:
如果有知道怎么解決的小伙伴麻煩留言告之一下,萬分謝謝。
完
掃描二維碼
獲取更多精彩
微卡智享
「 往期文章 」
制作一個Android Sqlite遠程運維小工具
VS2022 MAUI Hello World——Windows平臺及Android平臺效果
Android BaseQuickAdapter3.0.4版本二級列表的使用及遇到的問題
總結(jié)
以上是生活随笔為你收集整理的C++ OpenCV生成九宫格图像的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SAP学习培训的客户端安装
- 下一篇: 高斯函数和C++简单实现