zoukankan      html  css  js  c++  java
  • OpenCL + OpenCV 图像旋转

    ▶ 使用 OpenCV 从文件读取彩色的 png 图像,旋转一定角度以后写回文件

    ● 代码,核函数

     1 // rotate.cl                                  
     2 //__constant  sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_CLAMP;// 设备采样器,可以启用,并删除函数 imageRotate 中的采样器参数
     3 
     4 __kernel void imageRotate(__read_only image2d_t inputImage, __write_only image2d_t outputImage, float angle, sampler_t sampler)
     5 {
     6     const int width = get_image_width(inputImage), height = get_image_height(inputImage);
     7     const int halfWidth = width / 2, halfHeight = height / 2;
     8     const int x = get_global_id(0), y = get_global_id(1);
     9     const int xt = x - halfWidth, yt = y - halfHeight;
    10     const float sinFactor = sin(angle), cosFactor = cos(angle);
    11 
    12     float2 readCoord = (float2)(halfWidth + cosFactor * xt - sinFactor * yt, readCoord.y = halfHeight + sinFactor * xt + cosFactor * yt);
    13     float4 value = read_imagef(inputImage, sampler, readCoord);
    14     write_imagef(outputImage, (int2)(x, y), value);
    15     return;
    16 }

    ● 代码,分三通道分别旋转

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <cl.h>
      4 #include <opencv.hpp>
      5 #include <opencv2corecvstd.hpp>   // namespace cv 的定义
      6 
      7 #pragma warning(disable : 4996)     // 解封OPenCL1.2
      8 
      9 using namespace cv;
     10 
     11 const char *sourceProgram = "D:/Code/OpenCL/rotate.cl";// 核函数文件
     12 const char *imagePath = "D:\input.png";
     13 const float angle = 3.14f / 4;
     14 
     15 int readSource(const char* kernelPath, char **output)// 读取文本文件,存储为 char *
     16 {
     17     FILE *fp;
     18     int size;
     19     fopen_s(&fp, kernelPath, "rb");
     20     if (!fp)
     21     {
     22         printf("Open kernel file failed
    ");
     23         exit(-1);
     24     }
     25     if (fseek(fp, 0, SEEK_END) != 0)
     26     {
     27         printf("Seek end of file faildd
    ");
     28         exit(-1);
     29     }
     30     if ((size = ftell(fp)) < 0)
     31     {
     32         printf("Get file position failed
    ");
     33         exit(-1);
     34     }
     35     rewind(fp);
     36     if ((*output = (char *)malloc(size + 1)) == NULL)
     37     {
     38         printf("Allocate space failed
    ");
     39         exit(-1);
     40     }    
     41     fread((void*)*output, 1, size, fp);
     42     fclose(fp);
     43     (*output)[size] = '';
     44     printf("readSource succeed, program file: %s
    ", kernelPath);
     45     return size;
     46 }
     47 
     48 int main()
     49 {
     50     // 准备平台,设备,上下文,命令队列部分    
     51     cl_int status;
     52     cl_uint nPlatform;
     53     clGetPlatformIDs(0, NULL, &nPlatform);
     54     cl_platform_id *listPlatform = (cl_platform_id*)malloc(nPlatform * sizeof(cl_platform_id));
     55     clGetPlatformIDs(nPlatform, listPlatform, NULL);
     56     cl_uint nDevice = 0;
     57     clGetDeviceIDs(listPlatform[0], CL_DEVICE_TYPE_ALL, 0, NULL, &nDevice);
     58     cl_device_id *listDevice = (cl_device_id*)malloc(nDevice * sizeof(cl_device_id));
     59     clGetDeviceIDs(listPlatform[0], CL_DEVICE_TYPE_ALL, nDevice, listDevice, NULL);
     60     cl_context context = clCreateContext(NULL, nDevice, listDevice, NULL, NULL, &status);    
     61     cl_command_queue queue = clCreateCommandQueue(context, listDevice[0], 0, &status);                          // OpenCL1.2
     62     //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
     63     //                                            CL_QUEUE_SIZE, CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE,
     64     //                                            0};
     65     //cl_command_queue queue = clCreateCommandQueueWithProperties(context, listDevice[0], &queueProp, &status); // 第三个参数 queueProp 各种改都会报内存越界 0xC0000005
     66 
     67     // 图片相关
     68     Mat image, channel[3];
     69     image = imread(imagePath);
     70     split(image, channel);                          // 拆分为三通道,分别旋转后拼合
     71     const int imageHeight = image.rows, imageWidth = image.cols;
     72     unsigned char *imageData = (unsigned char*)malloc(sizeof(unsigned char) * imageHeight * imageWidth);
     73     
     74     cl_image_format format;
     75     format.image_channel_order = CL_R;              // 单通道
     76     format.image_channel_data_type = CL_UNORM_INT8; // 无符号 8 位整形,0 ~ 255
     77     cl_image_desc desc;
     78     desc.image_type = CL_MEM_OBJECT_IMAGE2D;        // 可以 memset(desc,sizeof(cl_image_desc)); 后仅对前三项赋值
     79     desc.image_width = imageWidth;
     80     desc.image_height = imageHeight;
     81     desc.image_depth = 0;
     82     desc.image_array_size = 0;
     83     desc.image_row_pitch = 0;
     84     desc.image_slice_pitch = 0;
     85     desc.num_mip_levels = 0;
     86     desc.num_samples = 0;
     87     desc.buffer = NULL;
     88     cl_mem d_inputImage = clCreateImage(context, CL_MEM_READ_ONLY, &format, &desc, NULL, &status);
     89     cl_mem d_outputImage = clCreateImage(context, CL_MEM_WRITE_ONLY, &format, &desc, NULL, &status);
     90 
     91     // 采样器
     92     cl_sampler sampler = clCreateSampler(context, CL_FALSE, CL_ADDRESS_CLAMP_TO_EDGE, CL_FILTER_NEAREST, &status);  // OpenCL1.2
     93     //cl_sampler_properties samplerProp[7] = {CL_SAMPLER_NORMALIZED_COORDS, CL_FALSE,                               // OpenCL2.0
     94     //                                        CL_SAMPLER_ADDRESSING_MODE, CL_ADDRESS_CLAMP_TO_EDGE, 
     95     //                                        CL_SAMPLER_FILTER_MODE, CL_FILTER_NEAREST,
     96     //                                        0};             
     97     //cl_sampler sampler = clCreateSamplerWithProperties(context, samplerProp, &status);                            // 也是内存越界,用不了
     98 
     99     // 程序和内核
    100     char* source = NULL;
    101     const size_t lenSource = readSource(sourceProgram, &source);
    102     cl_program program = clCreateProgramWithSource(context, 1, (const char **)&source, &lenSource, &status);
    103     clBuildProgram(program, 1, listDevice, NULL, NULL, NULL);
    104     cl_kernel kernel = clCreateKernel(program, "imageRotate", &status);    
    105     clSetKernelArg(kernel, 0, sizeof(cl_mem), &d_inputImage);
    106     clSetKernelArg(kernel, 1, sizeof(cl_mem), &d_outputImage);
    107     clSetKernelArg(kernel, 2, sizeof(cl_float), &angle);
    108     clSetKernelArg(kernel, 3, sizeof(cl_sampler), &sampler);
    109     size_t origin[3] = { 0, 0, 0 }, region[3] = { imageWidth, imageHeight, 1 };// 拷贝图片缓冲区时使用的起点和范围参数
    110     size_t globalSize[2] = { imageWidth, imageHeight };
    111 
    112     for (int i = 0; i < 3; i++)// 分三个通道拷入缓冲区,执行旋转操作,拷回内存
    113     {
    114         memcpy(imageData, channel[i].data, sizeof(unsigned char) * imageHeight * imageWidth);
    115         clEnqueueWriteImage(queue, d_inputImage, CL_TRUE, origin, region, 0, 0, imageData, 0, NULL, NULL);
    116         clEnqueueNDRangeKernel(queue, kernel, 2, NULL, globalSize, NULL, 0, NULL, NULL);
    117         clEnqueueReadImage(queue, d_outputImage, CL_TRUE, origin, region, 0, 0, imageData, 0, NULL, NULL);
    118         memcpy(channel[i].data, imageData, sizeof(unsigned char) * imageHeight * imageWidth);
    119     }
    120     
    121     merge(channel, 3, image);// 合并通道,结果写入文件,在窗口中展示结果
    122     imwrite("D:/output.png", image);
    123     imshow("Result", image);
    124     waitKey(0);
    125 
    126     free(listPlatform);
    127     free(listDevice);
    128     clReleaseContext(context);
    129     clReleaseMemObject(d_inputImage);
    130     clReleaseMemObject(d_outputImage);
    131     clReleaseCommandQueue(queue);
    132     clReleaseProgram(program);
    133     clReleaseKernel(kernel);    
    134     //getchar();
    135     return 0;
    136 }

    ● 代码,四个通道同时操作,注意图片读入和输出的时候只有三个通道,需要进行调整

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <cl.h>
      4 #include <opencv.hpp>
      5 #include <opencv2corecvstd.hpp>   // namespace cv 的定义
      6 
      7 #pragma warning(disable : 4996)     // 解封OPenCL1.2
      8 
      9 using namespace cv;
     10 
     11 const char *sourceProgram = "D:/Code/OpenCL/rotate.cl";// 核函数文件
     12 const char *imagePath = "D:/input.png";
     13 const float angle = 3.14f / 4;
     14 
     15 int readSource(const char* kernelPath, char **output)// 读取文本文件,存储为 char *
     16 {
     17     FILE *fp;
     18     int size;
     19     fopen_s(&fp, kernelPath, "rb");
     20     if (!fp)
     21     {
     22         printf("Open kernel file failed
    ");
     23         exit(-1);
     24     }
     25     if (fseek(fp, 0, SEEK_END) != 0)
     26     {
     27         printf("Seek end of file faildd
    ");
     28         exit(-1);
     29     }
     30     if ((size = ftell(fp)) < 0)
     31     {
     32         printf("Get file position failed
    ");
     33         exit(-1);
     34     }
     35     rewind(fp);
     36     if ((*output = (char *)malloc(size + 1)) == NULL)
     37     {
     38         printf("Allocate space failed
    ");
     39         exit(-1);
     40     }    
     41     fread((void*)*output, 1, size, fp);
     42     fclose(fp);
     43     (*output)[size] = '';
     44     printf("readSource succeed, program file: %s
    ", kernelPath);
     45     return size;
     46 }
     47 
     48 int main()
     49 {
     50     // 准备平台,设备,上下文,命令队列部分    
     51     cl_int status;
     52     cl_uint nPlatform;
     53     clGetPlatformIDs(0, NULL, &nPlatform);
     54     cl_platform_id *listPlatform = (cl_platform_id*)malloc(nPlatform * sizeof(cl_platform_id));
     55     clGetPlatformIDs(nPlatform, listPlatform, NULL);
     56     cl_uint nDevice = 0;
     57     clGetDeviceIDs(listPlatform[0], CL_DEVICE_TYPE_ALL, 0, NULL, &nDevice);
     58     cl_device_id *listDevice = (cl_device_id*)malloc(nDevice * sizeof(cl_device_id));
     59     clGetDeviceIDs(listPlatform[0], CL_DEVICE_TYPE_ALL, nDevice, listDevice, NULL);
     60     cl_context context = clCreateContext(NULL, nDevice, listDevice, NULL, NULL, &status);    
     61     cl_command_queue queue = clCreateCommandQueue(context, listDevice[0], 0, &status);// OpenCL1.2    
     62 
     63     // 图片相关
     64     Mat image = imread(imagePath);
     65     const int imageHeight = image.rows, imageWidth = image.cols;
     66     unsigned char *imageData = (unsigned char*)malloc(sizeof(unsigned char) * imageHeight * imageWidth * 4);
     67     
     68     for (int i = 0; i < imageWidth * imageHeight; i++)// imread 读进来只有 RGB 三个通道(可能跟图片本身有关),要补成 4 个通道
     69     {
     70         imageData[4 * i + 0] = image.data[3 * i + 2];//R
     71         imageData[4 * i + 1] = image.data[3 * i + 1];//G
     72         imageData[4 * i + 2] = image.data[3 * i + 0];//B
     73         imageData[4 * i + 3] = 255;                  //A
     74     }
     75 
     76     cl_image_format format;
     77     format.image_channel_order = CL_RGBA;            // 合并通道
     78     format.image_channel_data_type = CL_UNORM_INT8; // 无符号 8 位整形,0 ~ 255
     79     cl_image_desc desc;
     80     desc.image_type = CL_MEM_OBJECT_IMAGE2D;        // 可以 memset(desc,sizeof(cl_image_desc)); 后仅对前三项赋值
     81     desc.image_width = imageWidth;
     82     desc.image_height = imageHeight;
     83     desc.image_depth = 0;
     84     desc.image_array_size = 0;
     85     desc.image_row_pitch = 0;
     86     desc.image_slice_pitch = 0;
     87     desc.num_mip_levels = 0;
     88     desc.num_samples = 0;
     89     desc.buffer = NULL;
     90     cl_mem d_inputImage = clCreateImage(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, &format, &desc, imageData, &status);// 输入图片直接在主机上
     91     cl_mem d_outputImage = clCreateImage(context, CL_MEM_WRITE_ONLY, &format, &desc, NULL, &status);    
     92 
     93     // 采样器
     94     cl_sampler sampler = clCreateSampler(context, CL_FALSE, CL_ADDRESS_CLAMP_TO_EDGE, CL_FILTER_NEAREST, &status);  // OpenCL1.2
     95 
     96     // 程序和内核
     97     char* source = NULL;
     98     const size_t lenSource = readSource(sourceProgram, &source);
     99     cl_program program = clCreateProgramWithSource(context, 1, (const char **)&source, &lenSource, &status);
    100     clBuildProgram(program, 1, listDevice, NULL, NULL, NULL);
    101     cl_kernel kernel = clCreateKernel(program, "imageRotate", &status);    
    102     clSetKernelArg(kernel, 0, sizeof(cl_mem), &d_inputImage);
    103     clSetKernelArg(kernel, 1, sizeof(cl_mem), &d_outputImage);
    104     clSetKernelArg(kernel, 2, sizeof(cl_float), &angle);
    105     clSetKernelArg(kernel, 3, sizeof(cl_sampler), &sampler);
    106     size_t origin[3] = { 0, 0, 0 }, region[3] = { imageWidth, imageHeight, 1 };// 拷贝图片缓冲区时使用的起点和范围参数
    107     size_t globalSize[2] = { imageWidth, imageHeight };
    108 
    109     clEnqueueNDRangeKernel(queue, kernel, 2, NULL, globalSize, NULL, 0, NULL, NULL);
    110     clEnqueueReadImage(queue, d_outputImage, CL_TRUE, origin, region, 0, 0, imageData, 0, NULL, NULL);
    111 
    112     for (int i = 0; i < imageWidth * imageHeight; i++)// 去掉第 4 个通道,返回 image 中
    113     {
    114         image.data[3 * i + 0] = imageData[4 * i + 2];//B
    115         image.data[3 * i + 1] = imageData[4 * i + 1];//G
    116         image.data[3 * i + 2] = imageData[4 * i + 0];//R        
    117     }    
    118     
    119     imwrite("D:/output.png", image);
    120     imshow("Result", image);
    121     waitKey(0);
    122 
    123     free(listPlatform);
    124     free(listDevice);
    125     clReleaseContext(context);
    126     clReleaseMemObject(d_inputImage);
    127     clReleaseMemObject(d_outputImage);
    128     clReleaseCommandQueue(queue);
    129     clReleaseProgram(program);
    130     clReleaseKernel(kernel);    
    131     //getchar();
    132     return 0;
    133 }

    ● 输入、输出结果,顺时针转 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:WindowsSystem32 里边那个相同库就好了,可以完全使用 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)代码还看不了。

  • 相关阅读:
    NGINX -- 详解Nginx几种常见实现301重定向方法上的区别
    数据库外键的使用以及优缺点
    phpok -- 域名问题
    Sql Server系列:多表连接查询
    Go -- cron定时任务的用法
    JavaScript -- 清除缓存
    sql CAST用法
    Mock -- 数据模拟
    EsLint入门
    citus real-time 分析demo( 来自官方文档)
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/9014015.html
Copyright © 2011-2022 走看看