OpenCL + OpenCV 图像旋转
▶ 使用 OpenCV 从文件读取彩色的 png 图像,旋转一定角度以后写回文件
● 代码,核函数
// rotate.cl
//__constant sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_CLAMP;// 设备采样器,可以启用,并删除函数 imageRotate 中的采样器参数 __kernel void imageRotate(__read_only image2d_t inputImage, __write_only image2d_t outputImage, float angle, sampler_t sampler)
{
const int width = get_image_width(inputImage), height = get_image_height(inputImage);
const int halfWidth = width / , halfHeight = height / ;
const int x = get_global_id(), y = get_global_id();
const int xt = x - halfWidth, yt = y - halfHeight;
const float sinFactor = sin(angle), cosFactor = cos(angle); float2 readCoord = (float2)(halfWidth + cosFactor * xt - sinFactor * yt, readCoord.y = halfHeight + sinFactor * xt + cosFactor * yt);
float4 value = read_imagef(inputImage, sampler, readCoord);
write_imagef(outputImage, (int2)(x, y), value);
return;
}
● 代码,分三通道分别旋转
#include <stdio.h>
#include <stdlib.h>
#include <cl.h>
#include <opencv.hpp>
#include <opencv2\core\cvstd.hpp> // namespace cv 的定义 #pragma warning(disable : 4996) // 解封OPenCL1.2 using namespace cv; const char *sourceProgram = "D:/Code/OpenCL/rotate.cl";// 核函数文件
const char *imagePath = "D:\\input.png";
const float angle = 3.14f / ; int readSource(const char* kernelPath, char **output)// 读取文本文件,存储为 char *
{
FILE *fp;
int size;
fopen_s(&fp, kernelPath, "rb");
if (!fp)
{
printf("Open kernel file failed\n");
exit(-);
}
if (fseek(fp, , SEEK_END) != )
{
printf("Seek end of file faildd\n");
exit(-);
}
if ((size = ftell(fp)) < )
{
printf("Get file position failed\n");
exit(-);
}
rewind(fp);
if ((*output = (char *)malloc(size + )) == NULL)
{
printf("Allocate space failed\n");
exit(-);
}
fread((void*)*output, , size, fp);
fclose(fp);
(*output)[size] = '\0';
printf("readSource succeed, program file: %s\n", kernelPath);
return size;
} int main()
{
// 准备平台,设备,上下文,命令队列部分
cl_int status;
cl_uint nPlatform;
clGetPlatformIDs(, NULL, &nPlatform);
cl_platform_id *listPlatform = (cl_platform_id*)malloc(nPlatform * sizeof(cl_platform_id));
clGetPlatformIDs(nPlatform, listPlatform, NULL);
cl_uint nDevice = ;
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, , NULL, &nDevice);
cl_device_id *listDevice = (cl_device_id*)malloc(nDevice * sizeof(cl_device_id));
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, nDevice, listDevice, NULL);
cl_context context = clCreateContext(NULL, nDevice, listDevice, NULL, NULL, &status);
cl_command_queue queue = clCreateCommandQueue(context, listDevice[], , &status); // OpenCL1.2
//cl_command_queue_properties queueProp[5] = {CL_QUEUE_PROPERTIES, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | CL_QUEUE_ON_DEVICE_DEFAULT,// OpenCL2.0
// CL_QUEUE_SIZE, CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE,
// 0};
//cl_command_queue queue = clCreateCommandQueueWithProperties(context, listDevice[0], &queueProp, &status); // 第三个参数 queueProp 各种改都会报内存越界 0xC0000005 // 图片相关
Mat image, channel[];
image = imread(imagePath);
split(image, channel); // 拆分为三通道,分别旋转后拼合
const int imageHeight = image.rows, imageWidth = image.cols;
unsigned char *imageData = (unsigned char*)malloc(sizeof(unsigned char) * imageHeight * imageWidth); cl_image_format format;
format.image_channel_order = CL_R; // 单通道
format.image_channel_data_type = CL_UNORM_INT8; // 无符号 8 位整形,0 ~ 255
cl_image_desc desc;
desc.image_type = CL_MEM_OBJECT_IMAGE2D; // 可以 memset(desc,sizeof(cl_image_desc)); 后仅对前三项赋值
desc.image_width = imageWidth;
desc.image_height = imageHeight;
desc.image_depth = ;
desc.image_array_size = ;
desc.image_row_pitch = ;
desc.image_slice_pitch = ;
desc.num_mip_levels = ;
desc.num_samples = ;
desc.buffer = NULL;
cl_mem d_inputImage = clCreateImage(context, CL_MEM_READ_ONLY, &format, &desc, NULL, &status);
cl_mem d_outputImage = clCreateImage(context, CL_MEM_WRITE_ONLY, &format, &desc, NULL, &status); // 采样器
cl_sampler sampler = clCreateSampler(context, CL_FALSE, CL_ADDRESS_CLAMP_TO_EDGE, CL_FILTER_NEAREST, &status); // OpenCL1.2
//cl_sampler_properties samplerProp[7] = {CL_SAMPLER_NORMALIZED_COORDS, CL_FALSE, // OpenCL2.0
// CL_SAMPLER_ADDRESSING_MODE, CL_ADDRESS_CLAMP_TO_EDGE,
// CL_SAMPLER_FILTER_MODE, CL_FILTER_NEAREST,
// 0};
//cl_sampler sampler = clCreateSamplerWithProperties(context, samplerProp, &status); // 也是内存越界,用不了 // 程序和内核
char* source = NULL;
const size_t lenSource = readSource(sourceProgram, &source);
cl_program program = clCreateProgramWithSource(context, , (const char **)&source, &lenSource, &status);
clBuildProgram(program, , listDevice, NULL, NULL, NULL);
cl_kernel kernel = clCreateKernel(program, "imageRotate", &status);
clSetKernelArg(kernel, , sizeof(cl_mem), &d_inputImage);
clSetKernelArg(kernel, , sizeof(cl_mem), &d_outputImage);
clSetKernelArg(kernel, , sizeof(cl_float), &angle);
clSetKernelArg(kernel, , sizeof(cl_sampler), &sampler);
size_t origin[] = { , , }, region[] = { imageWidth, imageHeight, };// 拷贝图片缓冲区时使用的起点和范围参数
size_t globalSize[] = { imageWidth, imageHeight }; for (int i = ; i < ; i++)// 分三个通道拷入缓冲区,执行旋转操作,拷回内存
{
memcpy(imageData, channel[i].data, sizeof(unsigned char) * imageHeight * imageWidth);
clEnqueueWriteImage(queue, d_inputImage, CL_TRUE, origin, region, , , imageData, , NULL, NULL);
clEnqueueNDRangeKernel(queue, kernel, , NULL, globalSize, NULL, , NULL, NULL);
clEnqueueReadImage(queue, d_outputImage, CL_TRUE, origin, region, , , imageData, , NULL, NULL);
memcpy(channel[i].data, imageData, sizeof(unsigned char) * imageHeight * imageWidth);
} merge(channel, , image);// 合并通道,结果写入文件,在窗口中展示结果
imwrite("D:/output.png", image);
imshow("Result", image);
waitKey(); free(listPlatform);
free(listDevice);
clReleaseContext(context);
clReleaseMemObject(d_inputImage);
clReleaseMemObject(d_outputImage);
clReleaseCommandQueue(queue);
clReleaseProgram(program);
clReleaseKernel(kernel);
//getchar();
return ;
}
● 代码,四个通道同时操作,注意图片读入和输出的时候只有三个通道,需要进行调整
#include <stdio.h>
#include <stdlib.h>
#include <cl.h>
#include <opencv.hpp>
#include <opencv2\core\cvstd.hpp> // namespace cv 的定义 #pragma warning(disable : 4996) // 解封OPenCL1.2 using namespace cv; const char *sourceProgram = "D:/Code/OpenCL/rotate.cl";// 核函数文件
const char *imagePath = "D:/input.png";
const float angle = 3.14f / ; int readSource(const char* kernelPath, char **output)// 读取文本文件,存储为 char *
{
FILE *fp;
int size;
fopen_s(&fp, kernelPath, "rb");
if (!fp)
{
printf("Open kernel file failed\n");
exit(-);
}
if (fseek(fp, , SEEK_END) != )
{
printf("Seek end of file faildd\n");
exit(-);
}
if ((size = ftell(fp)) < )
{
printf("Get file position failed\n");
exit(-);
}
rewind(fp);
if ((*output = (char *)malloc(size + )) == NULL)
{
printf("Allocate space failed\n");
exit(-);
}
fread((void*)*output, , size, fp);
fclose(fp);
(*output)[size] = '\0';
printf("readSource succeed, program file: %s\n", kernelPath);
return size;
} int main()
{
// 准备平台,设备,上下文,命令队列部分
cl_int status;
cl_uint nPlatform;
clGetPlatformIDs(, NULL, &nPlatform);
cl_platform_id *listPlatform = (cl_platform_id*)malloc(nPlatform * sizeof(cl_platform_id));
clGetPlatformIDs(nPlatform, listPlatform, NULL);
cl_uint nDevice = ;
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, , NULL, &nDevice);
cl_device_id *listDevice = (cl_device_id*)malloc(nDevice * sizeof(cl_device_id));
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, nDevice, listDevice, NULL);
cl_context context = clCreateContext(NULL, nDevice, listDevice, NULL, NULL, &status);
cl_command_queue queue = clCreateCommandQueue(context, listDevice[], , &status);// OpenCL1.2 // 图片相关
Mat image = imread(imagePath);
const int imageHeight = image.rows, imageWidth = image.cols;
unsigned char *imageData = (unsigned char*)malloc(sizeof(unsigned char) * imageHeight * imageWidth * ); for (int i = ; i < imageWidth * imageHeight; i++)// imread 读进来只有 RGB 三个通道(可能跟图片本身有关),要补成 4 个通道
{
imageData[ * i + ] = image.data[ * i + ];//R
imageData[ * i + ] = image.data[ * i + ];//G
imageData[ * i + ] = image.data[ * i + ];//B
imageData[ * i + ] = ; //A
} cl_image_format format;
format.image_channel_order = CL_RGBA; // 合并通道
format.image_channel_data_type = CL_UNORM_INT8; // 无符号 8 位整形,0 ~ 255
cl_image_desc desc;
desc.image_type = CL_MEM_OBJECT_IMAGE2D; // 可以 memset(desc,sizeof(cl_image_desc)); 后仅对前三项赋值
desc.image_width = imageWidth;
desc.image_height = imageHeight;
desc.image_depth = ;
desc.image_array_size = ;
desc.image_row_pitch = ;
desc.image_slice_pitch = ;
desc.num_mip_levels = ;
desc.num_samples = ;
desc.buffer = NULL;
cl_mem d_inputImage = clCreateImage(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, &format, &desc, imageData, &status);// 输入图片直接在主机上
cl_mem d_outputImage = clCreateImage(context, CL_MEM_WRITE_ONLY, &format, &desc, NULL, &status); // 采样器
cl_sampler sampler = clCreateSampler(context, CL_FALSE, CL_ADDRESS_CLAMP_TO_EDGE, CL_FILTER_NEAREST, &status); // OpenCL1.2 // 程序和内核
char* source = NULL;
const size_t lenSource = readSource(sourceProgram, &source);
cl_program program = clCreateProgramWithSource(context, , (const char **)&source, &lenSource, &status);
clBuildProgram(program, , listDevice, NULL, NULL, NULL);
cl_kernel kernel = clCreateKernel(program, "imageRotate", &status);
clSetKernelArg(kernel, , sizeof(cl_mem), &d_inputImage);
clSetKernelArg(kernel, , sizeof(cl_mem), &d_outputImage);
clSetKernelArg(kernel, , sizeof(cl_float), &angle);
clSetKernelArg(kernel, , sizeof(cl_sampler), &sampler);
size_t origin[] = { , , }, region[] = { imageWidth, imageHeight, };// 拷贝图片缓冲区时使用的起点和范围参数
size_t globalSize[] = { imageWidth, imageHeight }; clEnqueueNDRangeKernel(queue, kernel, , NULL, globalSize, NULL, , NULL, NULL);
clEnqueueReadImage(queue, d_outputImage, CL_TRUE, origin, region, , , imageData, , NULL, NULL); for (int i = ; i < imageWidth * imageHeight; i++)// 去掉第 4 个通道,返回 image 中
{
image.data[ * i + ] = imageData[ * i + ];//B
image.data[ * i + ] = imageData[ * i + ];//G
image.data[ * i + ] = imageData[ * i + ];//R
} imwrite("D:/output.png", image);
imshow("Result", image);
waitKey(); free(listPlatform);
free(listDevice);
clReleaseContext(context);
clReleaseMemObject(d_inputImage);
clReleaseMemObject(d_outputImage);
clReleaseCommandQueue(queue);
clReleaseProgram(program);
clReleaseKernel(kernel);
//getchar();
return ;
}
● 输入、输出结果,顺时针转 45 度,因为使用了最近邻采样,结果中锯齿比较严重
● 另一种解封旧 API 的方法,在 包含头文件 <cl.h> 前使用 #define CL_USE_DEPRECATED_OPENCL_1_2_APIS ,其中 1_2 可以改成 1_0,1_1 等(https://stackoverflow.com/questions/28500496/opencl-function-found-deprecated-by-visual-studio/28500846#28500846)
● 使用 cl_command_queue_properties 和函数 clCreateCommandQueueWithProperties 来创建命令队列,或是用 cl_sampler_properties 和函数 clCreateSamplerWithProperties 来创建采样器都失败了,报内存访问越界错误(0xC0000005),无论是按格式书写还是把 queueProp 改成 0,创建时第三个参数写成 &queueProp 都不行;有人说更新显卡驱动以后就好了(https://stackoverflow.com/questions/39864947/opencl-cl-out-of-host-memory-on-clcreatecommandqueuewithproperties-with-minima)。最后解决了,用 AMD APP SDK 下面的动态库 amdocl64.dll 替换掉 C:\Windows\System32 里边那个相同库就好了,可以完全使用 OpenCL2.0 的 API,不再报错。
● cv::imread 读入的图片是按照 [ R, G, B, R, G, B, R, G, B, ...] 存放的,在用 OpenCL处理之前需要进行一定的预处理,要么用 split 分解各通道为单独的图片,要么手工拆解,算完以后也要按照这种存放方式转回图像数据中。在发现通道个数和顺序的问题前,要么在调用函数 clCreateImage 的时候返回 -37,-38,-39,要么直接旋转得到像下面这样的图片。以后记得,如果出现这种交叉条纹的图像,有可能是通道交错导致的。
● 吐槽一下,网上能找到的 OpenCL + OpenCV 做图片旋转的基本上有几个版本(https://blog.csdn.net/c602273091/article/details/45418223,https://blog.csdn.net/icamera0/article/details/71598323,https://blog.csdn.net/jaccen2012/article/details/51367388)都是用 FreeImage 库把图像处理成灰度图来旋转的(参考了 刘文志等(2016). OpenCL 异构并行计算[M]. 的代码?),输出肯定是灰度图了,然后大家博客就相互抄吧,全是垃圾。好不容易找到一个彩色的(https://blog.csdn.net/Bob_Dong/article/details/64906734)代码还看不了。
OpenCL + OpenCV 图像旋转的更多相关文章
- OpenCV:OpenCV图像旋转的代码
OpenCV图像旋转的代码 cv::transpose( bfM, bfM ) 前提:使用两个矩阵Mat型进行下标操作是不行的,耗费的时间太长了.直接使用两个指针对拷贝才是王道.不知道和OpenCV比 ...
- opencv 图像旋转
理论 http://www.cnblogs.com/wangguchangqing/p/4045150.html 翻开任意一本图像处理的书,都会讲到图像的几何变换,这里面包括:仿射变换(affine ...
- OpenCV图像旋转
图像旋转是指图像按照某个位置转动一定角度的过程,旋转中图像仍保持这原始尺寸.图像旋转后图像的水平对称轴.垂直对称轴及中心坐标原点都可能会发生变换,因此需要对图像旋转中的坐标进行相应转换. 如下图: 假 ...
- OpenCV 图像旋转实现
1 旋转矩形 首先建议阅读图像旋转算法原理-旋转矩阵,这篇博客可以让你很好地理解图像中的每一个点是如何进行旋转操作的.其中涉及到了图像原点与笛卡尔坐标原点之间的相互转换以及点旋转的一些公式推导. 这里 ...
- opencv 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移
常常需要最图像进行仿射变换,仿射变换后,我们可能需要将原来图像中的特征点坐标进行重新计算,获得原来图像中例如眼睛瞳孔坐标的新的位置,用于在新得到图像中继续利用瞳孔位置坐标. 仿射变换在:http:// ...
- [opencv] 图像几何变换:旋转,缩放,斜切
几何变换 几何变换可以看成图像中物体(或像素)空间位置改变,或者说是像素的移动. 几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定 ...
- 基于c++和opencv底层的图像旋转
图像旋转:本质上是对旋转后的图片中的每个像素计算在原图的位置. 在opencv包里有自带的旋转函数,当你知道倾斜角度theta时: 用getRotationMatrix2D可得2X3的旋转变换矩阵 M ...
- OpenCV计算机视觉学习(11)——图像空间几何变换(图像缩放,图像旋转,图像翻转,图像平移,仿射变换,镜像变换)
如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 图像 ...
- 【OpenCV学习笔记】之六 手写图像旋转函数---万丈高楼平地起
话说,平凡之处显真格,这一点也没错! 比如,对旋转图像进行双线性插值,很简单吧? 可,对我,折腾了大半天,也没有达到预期效果! 尤其是三个误区让我抓瞎好久: 1,坐标旋转公式. 这东西,要用 ...
随机推荐
- Python学习-终端字体高亮显示
1.采用原生转义字符序列,对Windows有的版本不支持(比如win7),完美支持Linux 实现过程: 终端的字符颜色是用转义序列控制的,是文本模式下的系统显示功能,和具体的语言无关. 转义序列是以 ...
- ASP.NET 4.0尚未在 Web 服务器上注册
ASP.NET 4.0尚未在 Web 服务器上注册 解决方法 使用VS2010创建web应用程序时出现如下提示ASP.NET 4.0尚未在 Web 服务器上注册.为了使网站正确运行,可能需要手动将 W ...
- 实习第一天:static 声明的 变量和 方法
static 声明的 变量和 方法 既可以用类.变量或者类.方法来调用 order by表格:Store_Information表格 Name Sacles DAteAngeles 1500 19 ...
- leetcode:Same Tree【Python版】
1.p或q为None的情况用开始的两个if语句进行判断: 2.类中递归调用函数需要使用self进行调用: 3.代码很简洁,最后几行通过同时为None和同时非None的条件进行判断: # Definit ...
- dbt- 数据构建工具
dbt(数据构建工具)是一个命令行工具,只需编写select语句即可转换仓库中的数据. dbt处理将这些select语句转换为表和视图.DBT帮助做T的ELT(提取,加载和转换) 的过程-它不提取或加 ...
- .NET4.0框架退休日期逐渐临近
微软宣布了.NET框架4.0至4.5.1版本的生命周期终结计划. 2016年1月12日之后,所有的技术支持,包含安全和非安全的更新补丁,都将会停止.开发人员和用户到时候可以选择回退到.NET 3.5 ...
- TCC(Tiny C Compiler)介绍
TCC是一个超小.超快的标准C语言编译器.她可以从这里(http://bellard.org/tcc/)下载到:注意,要下载http://download.savannah.nongnu.org/re ...
- yii2 rbac权限管理学习笔记
下面介绍一个 yii2 的 Rbac 权限管理设置,闲话少说,直接上代码, 1.首先我们要在组件里面配置一下 Rbac ,如下所示(common/config/main-local.php或者main ...
- grep和egrep正则表达式
Linux上文本处理三剑客 grep :文本过滤( 模式:pattern) 工具 grep, egrep, fgrep (不支持正则表达式搜索,但搜索纯文本的数据最快) sed :stream edi ...
- linux上通过lighttpd上跑一个C语言的CGI小页面以及所遇到的坑
Common Gateway Interface如雷贯耳,遗憾的是一直以来都没玩过CGI,今天尝试一把.Tomcat可以是玩CGI的,但得改下配置.为了方便,直接使用一款更轻量级的web服务器ligh ...