了解JPEG数据格式的人应该easy想到。其对图像以8*8像素块大小进行切割压缩的方法非常好用并行处理的思想来实现。而其实英伟达的CUDA自v5.5開始也提供了JPEG编解码的演示样例。该演示样例存储在CUDA的SDK中,即CUDA的默认安装路径“C:\ProgramData\NVDIA
Corporation\CUDA Samples\v7.0\7_CUDALibraries\jpegNPP”(v后面的数字依据版本号的不同会变更)中。

该演示样例将图片数据进行了解码和再编码,因为解码仅仅是将数据转为YUV,我们假设要利用演示样例来将图像转为RGB数据还需进行YUV->RGB的转换工作。这也正是本篇文章要重点介绍的内容。

此外。因为演示样例本身存在一个bug,所以无法直接利用其解码压缩宽高比不同的图像,这个会在下文再次提到,并给出比較取巧的修复方法。这个bug已经上报给英伟达。英伟达回复将在下个版本号(也就是v7.0之后的版本号)修复这个bug。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VpeGluaHVt/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

转载请注明出处:http://blog.csdn.net/weixinhum/article/details/46683509

OK。以下就開始吧

因为我们须要改动DEMO的源代码。还是先到上面的路径下将jpegNPP目录备份出一份来。然后我们直接打开目录里面的vsproject。project的主要代码在jpegNPP.cpp中,到了

// Inverse DCT
for (int i = 0; i < 3; ++i)
{
NPP_CHECK_NPP(nppiDCTQuantInv8x8LS_JPEG_16s8u_C1R_NEW(apdDCT[i], aDCTStep[i],
apSrcImage[i], aSrcImageStep[i],
pdQuantizationTables + oFrameHeader.aQuantizationTableSelector[i] * 64,
aSrcSize[i],
pDCTState));
}

这段代码便已经实现了将JPEG图像进行解码转化为YUV数据的功能。YUV数据存储在apSrcImage[0],apSrcImage[1],apSrcImage[2]中,而其步长(通道宽度)分别存在aSrcImageStep[0],aSrcImageStep[1],aSrcImageStep[2]中,已知条件已经足够了。我们能够直接删掉上述所贴代码后面的全部代码(那部分代码是关于图像编码的),然后写一个CUDA处理函数将YUV转为RGB。

大致流程例如以下:

配置OpenCV环境并包括头文件(这一步并非必要的。仅仅是为了方便查看我们转出来的图像是不是对的,假设认为不是必需能够忽略掉。仅仅要知道输出RGB的数据指针和数据长度大小便可):

#include <opencv2/core/core.hpp>//OpenCV包括头文件
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
using namespace std;

编写代码实现YUV转RGB:

在上述所贴DEMOproject代码后面加上例如以下代码:

int pwidth = aSrcSize[0].width;
int pheight = aSrcSize[0].height; IplImage *drawimg;//数据输出图像
drawimg = cvCreateImage(cvSize(pwidth, pheight), 8, 3); Npp8u *Host_img;//主机内存
Npp8u *Device_img;//显卡内存
size_t mPitch;
NPP_CHECK_CUDA(cudaMallocPitch(&Device_img, &mPitch, pwidth * 3, pheight));//开辟显存空间以存储RGB数据 //unsigned char* imgdata = (unsigned char*)drawimg->imageData;
YCrCb2RGB(apSrcImage[0], apSrcImage[1], apSrcImage[2], pwidth, pheight, aSrcImageStep[0],
aSrcImageStep[1], aSrcImageStep[2], drawimg->widthStep / sizeof(uchar), Device_img, nMCUBlocksV, nMCUBlocksH); NPP_CHECK_CUDA(cudaHostAlloc(&Host_img, pwidth*pheight * 3, cudaHostAllocDefault));//分配主机锁页内存
NPP_CHECK_CUDA(cudaMemcpy(Host_img, Device_img, pwidth*pheight * 3, cudaMemcpyDeviceToHost));//拷贝显卡处理完图像到主机
drawimg->imageData = (char*)Host_img; cvShowImage("", drawimg);
cvWaitKey(0);
getchar();
for (int i = 0; i < 3; ++i)//内存释放
{
cudaFree(apSrcImage[i]);
cudaFree(apdDCT[i]);
cudaFreeHost(aphDCT[i]);
}
cudaFree(Device_img);
cudaFreeHost(Host_img);
cudaDeviceReset();
return EXIT_SUCCESS;

加入一个“CudaYCrCb.cu”文件来定义YCrCb2RGB函数的功能,至于怎样去设置.cu文件假设有疑问的话请參照之前的这篇文章的相关内容,此外YCrCb2RGB函数须要在jpegNPP.cpp文件头声明下。文件的内容例如以下:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "Endianess.h" __device__ unsigned char judge(int value)
{
if (value >= 0 && value <= 255)
{
return value;
}
else if (value>255)
{
return 255;
}
else
{
return 0;
}
} __global__ void YCrCb2RGBConver(unsigned char *Device_Y, unsigned char *Device_Cr, unsigned char *Device_Cb, unsigned char *Device_img, int width, int height, int YStep, int CrStep, int CbStep, int img_step, int nMCUBlocksV, int nMCUBlocksH)//处理核函数
{
//int tid = blockIdx.x*blockDim.x + threadIdx.x;
int row = blockIdx.y*blockDim.y + threadIdx.y;
int cols = blockIdx.x*blockDim.x + threadIdx.x; if (row >= height)
{
return;
}
if (cols >= width)
{
return;
} int Y = Device_Y[row*YStep + cols];
int U = Device_Cr[row / nMCUBlocksH*CrStep + cols / nMCUBlocksV] - 128;
int V = Device_Cb[row / nMCUBlocksH*CbStep + cols / nMCUBlocksV] - 128; Device_img[row*img_step + cols * 3 + 0] =
judge(Y + U + ((U * 198) >> 8));
Device_img[row*img_step + cols * 3 + 1] =
judge(Y - (((U * 88) >> 8) + ((V * 183) >> 8)));
Device_img[row*img_step + cols * 3 + 2] =
judge(Y + V + ((V * 103) >> 8));
} extern "C" int YCrCb2RGB(unsigned char *Device_Y, unsigned char *Device_Cr, unsigned char *Device_Cb, int width, int height, int YStep, int CrStep, int CbStep, int img_step, unsigned char *Device_data, int nMCUBlocksV, int nMCUBlocksH)//显卡处理函数
{
cudaEvent_t start, stop;
float time;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);
//这个部分可调
dim3 threads(16, 16);//线程块中的线程数1*1
//dim3 threads(256, 40);//线程块中的线程数1*1
dim3 blocks((width + threads.x - 1) / threads.x, (height + threads.y - 1) / threads.y);//线程块大小
YCrCb2RGBConver << <blocks, threads >> >(Device_Y, Device_Cr, Device_Cb, Device_data, width, height, YStep, CrStep, CbStep, img_step, nMCUBlocksV, nMCUBlocksH);//调用显卡处理数据
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&time, start, stop);
cudaEventDestroy(start);
cudaEventDestroy(stop);
printf("核函数消耗时间:%f\n", time);
return 0;
}

声明例如以下:

extern "C" int YCrCb2RGB(unsigned char *Device_Y, unsigned char *Device_Cr, unsigned char *Device_Cb,int width, int height, int YStep, int CrStep, int CbStep, int img_step, unsigned char *Device_data, int nMCUBlocksV, int nMCUBlocksH);//显卡处理函数 

到此,实现了文章题目的内容,对于前文提到的英伟达的DEMO本身存在的bug(解码压缩宽高比不同的图像内存报错),是因为压缩的宽高比弄错导致的,能够通过例如以下截图的方式进行改动。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VpeGluaHVt/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

CUDA 实现JPEG图像解码为RGB数据的更多相关文章

  1. 在NVIDIA A100 GPU中使用DALI和新的硬件JPEG解码器快速加载数据

    在NVIDIA A100 GPU中使用DALI和新的硬件JPEG解码器快速加载数据 如今,最流行的拍照设备智能手机可以捕获高达4K UHD的图像(3840×2160图像),原始数据超过25 MB.即使 ...

  2. opencv IplImage各参数详细介绍以及如何从一个JPEG图像数据指针转换得到IplImage

    这篇文章里介绍得最清楚了.http://blog.chinaunix.net/uid-22682903-id-1771421.html 关于颜色空间  RGB颜色空间已经非常熟悉了.HSV颜色空间需要 ...

  3. FFMPEG结构体分析:AVFrame(解码后的数据)

    https://blog.csdn.net/jxcr1984/article/details/52766524 本文转自: http://blog.csdn.net/leixiaohua1020/ar ...

  4. JPEG图像压缩算法流程详解

    JPEG图像压缩算法流程详解 JPEG代表Joint Photographic Experts Group(联合图像专家小组).此团队创立于1986年,1992年发布了JPEG的标准而在1994年获得 ...

  5. 【STM32H7教程】第57章 STM32H7硬件JPEG编解码基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第57章       STM32H7硬件JPEG编解码基础知识 ...

  6. 简单易用的图像解码库介绍 —— stb_image

    原文链接:简单易用的图像解码库介绍 -- stb_image 说到图像解码库,最容易想起的就是 libpng 和 libjpeg 这两个老牌图像解码库了. libpng 和 libjpeg 分别各自对 ...

  7. JPEG图像密写研究(一) JPEG图像文件结构

    [转载]转载自http://www.cnblogs.com/leaven/archive/2010/04/06/1705846.html JPEG压缩编码算法的主要计算步骤如下: (0) 8*8分块. ...

  8. ffmpeg最简单的解码保存YUV数据 <转>

    video的raw data一般都是YUV420p的格式,简单的记录下这个格式的细节,如有不对希望大家能指出.   YUV图像通常有两种格式,一种是packet 还有一种是planar    从字面上 ...

  9. 【原创】JPEG图像密写研究(三) 数据流译码

    [原创]这次更新比较慢,译码过程比想象中复杂一些,更主要是译出来的DCT系数无法确定是否正确,要想验证就需要再进行正向压缩编码,再次形成jpeg图像验证正确,后续工作正在开展,这里就说一说译码的主要思 ...

随机推荐

  1. Sublime插件开发——简单的代码模板插件

    最近一段一直使用sublime进行golang开发,整体感觉很不错,虽然比不上eclipse之类IDE强大,但是用起来很轻巧便捷,开发golang完全做够了.由于有一部分代码复用率很高,经常要用到,而 ...

  2. curl download zip file

    https://askubuntu.com/questions/285976/download-zip-file-with-curl-command

  3. J​a​y​r​o​c​k​.​J​s​o​n​读​取​j​s​o​n​数​据​(​n​e​t​)

    1 : 首 先 下 载 Jayrock.Json.dll 文 件 , 放 入 bin 目 录 中 : 地 址 : http://www.filediag.com/down/Jayrock.Json.d ...

  4. 缓存淘汰算法之FIFO

    前段时间去网易面试,被这个问题卡住,先做总结如下: 常用缓存淘汰算法 FIFO类:First In First Out,先进先出.判断被存储的时间,离目前最远的数据优先被淘汰. LRU类:Least ...

  5. pl/sql 函数及与存储过程的区别

    函数用于返回特定的数据,当建立函数时,在函数头部必须包含return子句.而在函数体内必须包含return语句返回的数据.我们可以使用create function来建立函数. 1).接下来通过一个案 ...

  6. Codeforces 899D Shovel Sale

    题目大意 给定正整数 $n$($2\le n\le 10^9$). 考虑无序整数对 $(x, y)$($1\le x,y\le n, x\ne y$). 求满足 「$x+y$ 结尾连续的 9 最多」的 ...

  7. [luoguP2761] 软件补丁问题(状压最短路)

    传送门 n <= 20 很小 所以可以状态压缩 然后因为可能存在环,所以不能DP 那么就用spfa找最短路 被位运算坑了,不清楚优先级一定要加括号 ——代码 #include <queue ...

  8. P2258 子矩阵 (搜索,动态规划)

    题目链接 Solution 搜索+DP. 刚好把搜索卡死的数据范围... 然后应该可以很容易想到枚举行的情况,然后分列去DP. 行的情况直接全排列即可,复杂度最高 \(O(C_{16}^{8})\). ...

  9. Visual Code 自定义插件安装位置

    修改快速方式通过传入方式启动 ,示例: D:\installed_green_soft\VSCode\Code.exe --extensions-dir "D:\installed_gree ...

  10. SpringBoot基础之MockMvc单元测试

    SpringBoot创建的Maven项目中,会默认添加spring-boot-starter-test依赖.在<5分钟快速上手SpringBoot>中编写的单元测试使用了MockMvc.本 ...