CUDA Samples: Image Process: BGR to Gray
生活随笔
收集整理的這篇文章主要介紹了
CUDA Samples: Image Process: BGR to Gray
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
在圖像處理中,顏色變換BGR到Gray,常見的一般有兩種計算方式,一種是基于浮點數(shù)計算,一種是基于性能優(yōu)化的通過移位的整數(shù)計算。
浮點數(shù)計算公式為:?gray =?0.1140 * B??+ 0.5870 * G + 0.2989 * R;
整數(shù)計算公式為: gray = (1868 * B + 9617 * G + 4899 * R) >> 14.
這里與OpenCV的實現(xiàn)方式一致,采用整數(shù)計算公式。
以下分別是C++和CUDA的BGR到Gray的實現(xiàn)測試代碼:
bgr2gray.cpp:
#include "funset.hpp"
#include <chrono>
#include "common.hpp"int bgr2gray_cpu(const unsigned char* src, int width, int height, unsigned char* dst, float* elapsed_time)
{TIME_START_CPUconst int R2Y{ 4899 }, G2Y{ 9617 }, B2Y{ 1868 }, yuv_shift{ 14 };for (int y = 0; y < height; ++y) {for (int x = 0; x < width; ++x) {dst[y * width + x] = (unsigned char)((src[y*width * 3 + 3 * x + 0] * B2Y +src[y*width * 3 + 3 * x + 1] * G2Y + src[y*width * 3 + 3 * x + 2] * R2Y) >> yuv_shift);}}TIME_END_CPUreturn 0;
}
bgr2gray.cu:
#include "funset.hpp"
#include <iostream>
#include <chrono>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include "common.hpp"/* __global__: 函數(shù)類型限定符;在設(shè)備上運行;在主機端調(diào)用,計算能力3.2及以上可以在
設(shè)備端調(diào)用;聲明的函數(shù)的返回值必須是void類型;對此類型函數(shù)的調(diào)用是異步的,即在
設(shè)備完全完成它的運行之前就返回了;對此類型函數(shù)的調(diào)用必須指定執(zhí)行配置,即用于在
設(shè)備上執(zhí)行函數(shù)時的grid和block的維度,以及相關(guān)的流(即插入<<< >>>運算符);
a kernel,表示此函數(shù)為內(nèi)核函數(shù)(運行在GPU上的CUDA并行計算函數(shù)稱為kernel(內(nèi)核函
數(shù)),內(nèi)核函數(shù)必須通過__global__函數(shù)類型限定符定義);*/
__global__ static void bgr2gray(const unsigned char* src, int B2Y, int G2Y, int R2Y, int shift, int width, int height, unsigned char* dst)
{/* gridDim: 內(nèi)置變量,用于描述線程網(wǎng)格的維度,對于所有線程塊來說,這個變量是一個常數(shù),用來保存線程格每一維的大小,即每個線程格中線程塊的數(shù)量.一個grid為三維,為dim3類型;blockDim: 內(nèi)置變量,用于說明每個block的維度與尺寸.為dim3類型,包含了block在三個維度上的尺寸信息;對于所有線程塊來說,這個變量是一個常數(shù),保存的是線程塊中每一維的線程數(shù)量;blockIdx: 內(nèi)置變量,變量中包含的值就是當(dāng)前執(zhí)行設(shè)備代碼的線程塊的索引;用于說明當(dāng)前thread所在的block在整個grid中的位置,blockIdx.x取值范圍是[0,gridDim.x-1],blockIdx.y取值范圍是[0, gridDim.y-1].為uint3類型,包含了一個block在grid中各個維度上的索引信息;threadIdx: 內(nèi)置變量,變量中包含的值就是當(dāng)前執(zhí)行設(shè)備代碼的線程索引;用于說明當(dāng)前thread在block中的位置;如果線程是一維的可獲取threadIdx.x,如果是二維的還可獲取threadIdx.y,如果是三維的還可獲取threadIdx.z;為uint3類型,包含了一個thread在block中各個維度的索引信息 */int x = threadIdx.x + blockIdx.x * blockDim.x;int y = threadIdx.y + blockIdx.y * blockDim.y;//if (x == 0 && y == 0) {// printf("%d, %d, %d, %d, %d, %d\n", width, height, B2Y, G2Y, R2Y, shift);//}if (x < width && y < height) {dst[y * width + x] = (unsigned char)((src[y*width * 3 + 3 * x + 0] * B2Y +src[y*width * 3 + 3 * x + 1] * G2Y + src[y*width * 3 + 3 * x + 2] * R2Y) >> shift);}
}int bgr2gray_gpu(const unsigned char* src, int width, int height, unsigned char* dst, float* elapsed_time)
{const int R2Y{ 4899 }, G2Y{ 9617 }, B2Y{ 1868 }, yuv_shift{ 14 };unsigned char *dev_src{ nullptr }, *dev_dst{ nullptr };// cudaMalloc: 在設(shè)備端分配內(nèi)存cudaMalloc(&dev_src, width * height * 3 * sizeof(unsigned char));cudaMalloc(&dev_dst, width * height * sizeof(unsigned char));/* cudaMemcpy: 在主機端和設(shè)備端拷貝數(shù)據(jù),此函數(shù)第四個參數(shù)僅能是下面之一:(1). cudaMemcpyHostToHost: 拷貝數(shù)據(jù)從主機端到主機端(2). cudaMemcpyHostToDevice: 拷貝數(shù)據(jù)從主機端到設(shè)備端(3). cudaMemcpyDeviceToHost: 拷貝數(shù)據(jù)從設(shè)備端到主機端(4). cudaMemcpyDeviceToDevice: 拷貝數(shù)據(jù)從設(shè)備端到設(shè)備端(5). cudaMemcpyDefault: 從指針值自動推斷拷貝數(shù)據(jù)方向,需要支持統(tǒng)一虛擬尋址(CUDA6.0及以上版本)cudaMemcpy函數(shù)對于主機是同步的 */cudaMemcpy(dev_src, src, width * height * 3 * sizeof(unsigned char), cudaMemcpyHostToDevice);/* cudaMemset: 存儲器初始化函數(shù),在GPU內(nèi)存上執(zhí)行。用指定的值初始化或設(shè)置設(shè)備內(nèi)存 */cudaMemset(dev_dst, 0, width * height * sizeof(unsigned char));TIME_START_GPU/* dim3: 基于uint3定義的內(nèi)置矢量類型,相當(dāng)于由3個unsigned int類型組成的結(jié)構(gòu)體,可表示一個三維數(shù)組,在定義dim3類型變量時,凡是沒有賦值的元素都會被賦予默認(rèn)值1 */// Note:每一個線程塊支持的最大線程數(shù)量為1024,即threads.x*threads.y必須小于等于1024dim3 threads(32, 32);dim3 blocks((width + 31) / 32, (height + 31) / 32);/* <<< >>>: 為CUDA引入的運算符,指定線程網(wǎng)格和線程塊維度等,傳遞執(zhí)行參數(shù)給CUDA編譯器和運行時系統(tǒng),用于說明內(nèi)核函數(shù)中的線程數(shù)量,以及線程是如何組織的;尖括號中這些參數(shù)并不是傳遞給設(shè)備代碼的參數(shù),而是告訴運行時如何啟動設(shè)備代碼,傳遞給設(shè)備代碼本身的參數(shù)是放在圓括號中傳遞的,就像標(biāo)準(zhǔn)的函數(shù)調(diào)用一樣;不同計算能力的設(shè)備對線程的總數(shù)和組織方式有不同的約束;必須先為kernel中用到的數(shù)組或變量分配好足夠的空間,再調(diào)用kernel函數(shù),否則在GPU計算時會發(fā)生錯誤,例如越界等 ;使用運行時API時,需要在調(diào)用的內(nèi)核函數(shù)名與參數(shù)列表直接以<<<Dg,Db,Ns,S>>>的形式設(shè)置執(zhí)行配置,其中:Dg是一個dim3型變量,用于設(shè)置grid的維度和各個維度上的尺寸.設(shè)置好Dg后,grid中將有Dg.x*Dg.y*Dg.z個block;Db是一個dim3型變量,用于設(shè)置block的維度和各個維度上的尺寸.設(shè)置好Db后,每個block中將有Db.x*Db.y*Db.z個thread;Ns是一個size_t型變量,指定各塊為此調(diào)用動態(tài)分配的共享存儲器大小,這些動態(tài)分配的存儲器可供聲明為外部數(shù)組(extern __shared__)的其他任何變量使用;Ns是一個可選參數(shù),默認(rèn)值為0;S為cudaStream_t類型,用于設(shè)置與內(nèi)核函數(shù)關(guān)聯(lián)的流.S是一個可選參數(shù),默認(rèn)值0. */// Note: 核函數(shù)不支持傳入?yún)?shù)為vector的data()指針,需要cudaMalloc和cudaMemcpy,因為vector是在主機內(nèi)存中bgr2gray << <blocks, threads >> >(dev_src, B2Y, G2Y, R2Y, yuv_shift, width, height, dev_dst);/* cudaDeviceSynchronize: kernel的啟動是異步的, 為了定位它是否出錯, 一般需要加上cudaDeviceSynchronize函數(shù)進行同步; 將會一直處于阻塞狀態(tài),直到前面所有請求的任務(wù)已經(jīng)被全部執(zhí)行完畢,如果前面執(zhí)行的某個任務(wù)失敗,將會返回一個錯誤;當(dāng)程序中有多個流,并且流之間在某一點需要通信時,那就必須在這一點處加上同步的語句,即cudaDeviceSynchronize;異步啟動reference: https://stackoverflow.com/questions/11888772/when-to-call-cudadevicesynchronize */cudaDeviceSynchronize();TIME_END_GPUcudaMemcpy(dst, dev_dst, width * height * sizeof(unsigned char), cudaMemcpyDeviceToHost);// cudaFree: 釋放設(shè)備上由cudaMalloc函數(shù)分配的內(nèi)存cudaFree(dev_dst);cudaFree(dev_src);return 0;
}
main.cpp:
#include "funset.hpp"
#include <random>
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <algorithm>
#include "common.hpp"int test_image_process_bgr2gray()
{const std::string image_name{ "E:/GitCode/CUDA_Test/test_data/images/lena.png" };cv::Mat mat = cv::imread(image_name);CHECK(mat.data);const int width{ 1513 }, height{ 1473 };cv::resize(mat, mat, cv::Size(width, height));std::unique_ptr<unsigned char[]> data1(new unsigned char[width * height]), data2(new unsigned char[width * height]);float elapsed_time1{ 0.f }, elapsed_time2{ 0.f }; // millisecondsCHECK(bgr2gray_cpu(mat.data, width, height, data1.get(), &elapsed_time1) == 0);CHECK(bgr2gray_gpu(mat.data, width, height, data2.get(), &elapsed_time2) == 0);cv::Mat dst(height, width, CV_8UC1, data1.get());cv::imwrite("E:/GitCode/CUDA_Test/test_data/images/bgr2gray_cpu.png", dst);cv::Mat dst2(height, width, CV_8UC1, data2.get());cv::imwrite("E:/GitCode/CUDA_Test/test_data/images/bgr2gray_gpu.png", dst2);fprintf(stdout, "image bgr to gray: cpu run time: %f ms, gpu run time: %f ms\n", elapsed_time1, elapsed_time2);CHECK(compare_result(data1.get(), data2.get(), width*height) == 0);return 0;
}
執(zhí)行結(jié)果如下:由結(jié)果可知,C++和CUDA實現(xiàn)結(jié)果是一致的。
GitHub:? https://github.com/fengbingchun/CUDA_Test
總結(jié)
以上是生活随笔為你收集整理的CUDA Samples: Image Process: BGR to Gray的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++11多线程中std::call_o
- 下一篇: Windows C++中__declsp