OpenCV使用pthread实现多线程加速处理图像(C++)
OpenCV使用pthread實現多線程加速處理圖像
【尊重原創,轉載請注明出處】https://blog.csdn.net/guyuealian/article/details/81912704
目錄
OpenCV使用pthread實現多線程加速處理圖像
1.pthread多線程加速
2.自己封裝的多線程cvThread類
? ? POSIX線程(POSIX threads),簡稱Pthreads,是線程的POSIX標準。Pthread是由POSIX提出的一套通用的線程庫,在linux平臺下,它被廣泛的支持,而在windows平臺下,需要下載pthreads-w32庫。之所以選擇Pthreads庫作為多線程處理庫,是因此Android NDK開發時,可以使用pthread在C++中實現多線程處理,這樣,可以方便OpenCV的圖像加速處理和算法的移植。
pthreads-w32在Windows的配置方法,可參考:
https://blog.csdn.net/qianchenglenger/article/details/16907821
pthreads-w32-2-9-1:https://download.csdn.net/download/guyuealian/10623897
其他版本的地址:ftp://sourceware.org/pub/pthreads-win32
關于pthread的用法,可參考這個PDF:
https://download.csdn.net/download/qq_21792169/9380962
1.pthread多線程加速
? ? ?下面是使用多線程(3個子線程)實現OpenCV圖像加速的的方法,基本思路就是:先將圖像分塊,比如分成3塊,每塊使用一個子線程進行處理,處理完后再合并成一塊圖像,這樣就實現了OpenCV多線程加速圖像處理的方法。
// pthreadDemo.cpp : 定義控制臺應用程序的入口點。 //#include <stdio.h> #include <pthread.h> #include <assert.h> #include <string> #include <opencv2/opencv.hpp> using namespace std; #define THREAD_NUMS 3/*paramThread用于傳遞線程需要的參數值*/ struct paramThread {int w;int h;uchar * data; };/******************************************************** * @brief : 多線程處理函數 * @param args : 多線程傳入的參數 * @return : void ********************************************************/ void * threadProcess(void* args) {pthread_t myid = pthread_self();paramThread *para = (paramThread *)args;int w = para->w;int h = para->h;cv::Mat image(h,w,CV_8UC3,(uchar *)para->data);//cv::cvtColor(image, image, cv::COLOR_BGR2RGB);cv::blur(image, image,cv::Size(7,7), cv::Point(-1, -1), cv::BORDER_REPLICATE);//printf("thread id = %d, w=%d, h=%d\n", myid, w,h);//cv::imshow("image", image); cv::waitKey(2000);pthread_exit(NULL);return NULL; }/******************************************************** * @brief : 實現圖像分割, * @param num : 分割個數 * @param type : 0:垂直分割(推薦),1:水平分割(不推薦) * @return : vector<cv::Mat> * PS:使用水平分割時(type=1),處理完后必須調用catImage進行拼接, * 使用垂直分割時(type=0),可以不進行catImage,因為是對原圖進行操作的 ********************************************************/ vector<cv::Mat> splitImage(cv::Mat image, int num,int type) {int rows = image.rows;int cols = image.cols;vector<cv::Mat> v;if (type == 0) {//垂直分割for (size_t i = 0; i < num; i++) {int star = rows / num*i;int end = rows / num*(i + 1);if (i == num - 1) {end = rows;}//cv::Mat b = image.rowRange(star, end);v.push_back(image.rowRange(star, end));}}else if (type == 1) {//水平分割for (size_t i = 0; i < num; i++){int star = cols / num*i;int end = cols / num*(i + 1);if (i == num - 1) {end = cols;}//cv::Mat b = image.colRange(star, end);/*解決水平分割的Bug:必須clone()*/v.push_back(image.colRange(star, end).clone());}}return v; }/******************************************************** * @brief : 實現圖像拼接, * @param v : * @param type : 0:垂直拼接,1:水平拼接 * @return : Mat ********************************************************/ cv::Mat catImage(vector<cv::Mat> v, int type) {cv::Mat dest= v.at(0);for (size_t i = 1; i < v.size(); i++){if (type == 0)//垂直拼接{cv::vconcat(dest, v.at(i), dest);}else if (type == 1)//水平拼接{cv::hconcat(dest, v.at(i), dest);}}return dest; }int main() {string path = "D:\\imageEnhance\\images\\test.jpg";cv::Mat src = cv::imread(path);printf("image size = w=%d, h=%d\n", src.cols, src.rows);cv::Mat image1 = src.clone();cv::Mat image2 = src.clone();cv::imshow("src", src); cv::waitKey(30);double T0 = static_cast<double>(cv::getTickCount());/*不使用多線程圖像處理*/cv::blur(image1, image1, cv::Size(7, 7));double T1 = static_cast<double>(cv::getTickCount());/*使用多線程圖像處理*/int type = 0;vector<cv::Mat> v = splitImage(image2, THREAD_NUMS, type);paramThread args[THREAD_NUMS];pthread_t pt[THREAD_NUMS]; //創建THREAD_NUMS個子線程for (size_t i = 0; i < THREAD_NUMS; i++){args[i].h = v.at(i).rows;args[i].w = v.at(i).cols;args[i].data = v.at(i).data;pthread_create(&pt[i], NULL, &threadProcess, (void *)(&args[i]));}/*等待全部子線程處理完畢*/for (size_t i = 0; i < THREAD_NUMS; i++){pthread_join(pt[i], NULL);}cv::Mat dest = catImage(v, type);double T2 = static_cast<double>(cv::getTickCount());printf(" run times = %3.3fms\n", (T1 - T0)*1000 / cv::getTickFrequency());printf("Thread run times = %3.3fms\n,", (T2 - T1)*1000 / cv::getTickFrequency());cv::imshow("dest", dest); cv::waitKey(30);cv::imshow("image2", image2); cv::waitKey(30);cv::waitKey(0);return 0; }? ? ?可以看到,對于一張3000*1877的大圖片,使用3個線程并行處理會比單線程處理快了將近4倍的速度。但比較尷尬的是,由于使用了圖像分塊處理,進行圖像blur模糊時,邊界會出現不規則的像素,這時拼接在一起,會導致圖像拼接的邊緣出現橫條的現象,見下面的模糊圖。
? ? ?一種決解的方法就是,可以在圖像分割時,根據kernel size的大小的適當padding像素,處理完再cutting這些padding的像素,這部分我還沒有實現,有興趣的可以搞搞哈。
? ? 這種使用pthread實現多線程圖像處理加速方法,比較適合算法過程跟邊界處理無關的情況,如RGB轉BGR,Gamma變換這些圖像處理操作。
2.自己封裝的多線程cvThread類
? ? 為了方面以后調用,這里封裝了一個cvThread類:
? ? cvThread.h:
#pragma once #include <stdio.h> #include <pthread.h> #include <assert.h> #include <string> #include <opencv2/opencv.hpp> using namespace std;class cvThread { public:cvThread();~cvThread();/*paramThread用于傳遞線程需要的參數值*/struct paramThread{int w;int h;uchar * data;};/********************************************************* @brief : 多線程要處理的圖像操作* @param args : 多線程傳入的參數* @return : void********************************************************/static void * cvThreadBlur(void* args);/********************************************************* @brief : 多線程處理函數* @param image : 輸入/輸出Mat圖像* @param type : 分割類型0:垂直分割(推薦),1:水平分割(不推薦)* @param thread_num : 多線程個數* @return : void********************************************************/void cvThreadProcess(cv::Mat &image, const int type, const int thread_num);void cvThreadProcess(cv::Mat &image);/********************************************************* @brief : 實現圖像分割,* @param num : 分割個數* @param type : 0:垂直分割(推薦),1:水平分割(不推薦)* @return : vector<cv::Mat>* PS:使用水平分割時(type=1),處理完后必須調用catImage進行拼接,* 使用垂直分割時(type=0),可以不進行catImage,因為是對原圖進行操作的********************************************************/vector<cv::Mat> splitImage(cv::Mat image, int num, int type);/********************************************************* @brief : 實現圖像拼接,* @param v :* @param type : 0:垂直拼接,1:水平拼接* @return : Mat********************************************************/cv::Mat catImage(vector<cv::Mat> v, int type); };? ? cvThread.cpp:
#include "cvThread.h"cvThread::cvThread() { }cvThread::~cvThread() { } /******************************************************** * @brief : 多線程處理函數 * @param args : 多線程傳入的參數 * @return : void ********************************************************/ void * cvThread::cvThreadBlur(void* args) {pthread_t myid = pthread_self();paramThread *para = (paramThread *)args;int w = para->w;int h = para->h;cv::Mat image(h, w, CV_8UC3, (uchar *)para->data);/***************************************************//*這里實現多線程要處理的圖像操作*/cv::blur(image, image, cv::Size(7, 7), cv::Point(-1, -1), cv::BORDER_REPLICATE);/***************************************************/pthread_exit(NULL);return NULL; }#define THREAD_NUMS 4 void cvThread::cvThreadProcess(cv::Mat &image) {/*使用多線程圖像處理*/int type = 0;vector<cv::Mat> v = splitImage(image, THREAD_NUMS, type);paramThread args[THREAD_NUMS];pthread_t pt[THREAD_NUMS]; //創建thread_num個子線程for (size_t i = 0; i < THREAD_NUMS; i++){args[i].h = v.at(i).rows;args[i].w = v.at(i).cols;args[i].data = v.at(i).data;pthread_create(&pt[i], NULL, &cvThreadBlur, (void *)(&args[i]));}/*等待全部子線程處理完畢*/for (size_t i = 0; i < THREAD_NUMS; i++){pthread_join(pt[i], NULL);}cv::Mat dest = catImage(v, type); }void cvThread::cvThreadProcess(cv::Mat &image,const int type=0,const int thread_num=4) {/*使用多線程圖像處理*/vector<cv::Mat> v = splitImage(image, thread_num, type);paramThread *args = new paramThread[thread_num];pthread_t *pt = new pthread_t[thread_num]; //創建thread_num個子線程for (size_t i = 0; i < thread_num; i++){args[i].h = v.at(i).rows;args[i].w = v.at(i).cols;args[i].data = v.at(i).data;pthread_create(&pt[i], NULL, &cvThreadBlur, (void *)(&args[i]));}/*等待全部子線程處理完畢*/for (size_t i = 0; i < thread_num; i++){pthread_join(pt[i], NULL);}/*PS:使用水平分割時(type = 1),處理完后必須調用catImage進行拼接,使用垂直分割時(type = 0),可以不進行catImage,因為是對原圖進行操作的*/if (type==1){image = catImage(v, type);}delete []args;delete []pt; } /******************************************************** * @brief : 實現圖像分割, * @param num : 分割個數 * @param type : 0:垂直分割(推薦),1:水平分割(不推薦) * @return : vector<cv::Mat> * PS:使用水平分割時(type=1),處理完后必須調用catImage進行拼接, * 使用垂直分割時(type=0),可以不進行catImage,因為是對原圖進行操作的 ********************************************************/ vector<cv::Mat> cvThread::splitImage(cv::Mat image, int num, int type) {int rows = image.rows;int cols = image.cols;vector<cv::Mat> v;if (type == 0) {//垂直分割for (size_t i = 0; i < num; i++) {int star = rows / num*i;int end = rows / num*(i + 1);if (i == num - 1) {end = rows;}//cv::Mat b = image.rowRange(star, end);v.push_back(image.rowRange(star, end));}}else if (type == 1) {//水平分割for (size_t i = 0; i < num; i++) {int star = cols / num*i;int end = cols / num*(i + 1);if (i == num - 1) {end = cols;}//cv::Mat b = image.colRange(star, end);/*解決水平分割的Bug:必須clone()*/v.push_back(image.colRange(star, end).clone());}}return v; }/******************************************************** * @brief : 實現圖像拼接, * @param v : * @param type : 0:垂直拼接,1:水平拼接 * @return : Mat ********************************************************/ cv::Mat cvThread::catImage(vector<cv::Mat> v, int type) {cv::Mat dest = v.at(0);for (size_t i = 1; i < v.size(); i++){if (type == 0)//垂直拼接{cv::vconcat(dest, v.at(i), dest);}else if (type == 1)//水平拼接{cv::hconcat(dest, v.at(i), dest);}}return dest; }? ? 測試方法main.cpp:
#include <stdio.h> #include <pthread.h> #include <assert.h> #include <string> #include <opencv2/opencv.hpp> #include "cvThread.h" using namespace std; int main() {string path = "D:\\imageEnhance\\images\\8-1.jpg";cv::Mat src = cv::imread(path);printf("image size = w=%d, h=%d\n", src.cols, src.rows);cv::Mat image1 = src.clone();cv::Mat image2 = src.clone();cv::imshow("src", src); cv::waitKey(30);double T0 = static_cast<double>(cv::getTickCount());/*不使用多線程圖像處理*/cv::blur(image1, image1, cv::Size(7, 7));double T1 = static_cast<double>(cv::getTickCount());/*使用多線程圖像處理*/cvThread cvth;cvth.cvThreadProcess(image2,0,4);double T2 = static_cast<double>(cv::getTickCount());printf(" run times = %3.3fms\n", (T1 - T0)*1000 / cv::getTickFrequency());printf("Thread run times = %3.3fms\n,", (T2 - T1)*1000 / cv::getTickFrequency());cv::imshow("image1", image1); cv::waitKey(30);cv::imshow("image2", image2); cv::waitKey(30);cv::waitKey(0);return 0; }?
總結
以上是生活随笔為你收集整理的OpenCV使用pthread实现多线程加速处理图像(C++)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++常用方法笔记资料
- 下一篇: C++实现线程安全的单例模式