opencl4格环视拼接Beta0.5

opencl4格环视拼接Beta0.5

以下内容是单一输入的4格环视拼接,用于进行基础讲解 :

(基于代码 kernel5.cl,opencl_test02.cpp)

在这里插入图片描述

1.opencl中kernel通过 get_global_id函数得到循环的维度信息.

const int ix = get_global_id(1);
const int iy = get_global_id(0);

这个结构中参考逻辑代码中的配置信息:

	//set local and global workgroup sizes  
	size_t localws[2] = { 3, 3 };
	size_t globalws[2] = { height,width };

可以看出其循环体系是先循环图像高度height再循环图像宽度width,其逻辑结构可以认为参考下面代码:

for(i in height){
    for(j in width){
        //code;
    }
}

2.组合出被操纵的图像宽度大小.

const sampler_t a_sampler = CLK_NORMALIZED_COORDS_FALSE| CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;
__kernel void img_rotate(
	__read_only image2d_t sourceImage,
    __write_only image2d_t outputImage,
	int w,
	int h) {
    //code;
    const int w_dest = (w+2*h);

代码中可见kernel传入的图像有两幅,源图像高度和宽度被传入(其实可以直接用image2d的信息计算出来),kernel代码中的w_dest为输出图片的大小.逻辑代码中也有说明:

cl_image_format format_out;
format_out.image_channel_order = CL_BGRA;
format_out.image_channel_data_type = CL_UNSIGNED_INT8;

cl_image_desc desc_out;
desc_out.image_type = CL_MEM_OBJECT_IMAGE2D;
desc_out.image_width = width + 2 * height;
desc_out.image_height = width + 2 * height;
desc_out.image_depth = 0;
desc_out.image_array_size = 0;
desc_out.image_row_pitch = 0;
desc_out.image_slice_pitch = 0;
desc_out.num_mip_levels = 0;
desc_out.num_samples = 0;
desc_out.buffer = NULL;

//code;
memOutputBuffer = clCreateImage(Context, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, &format_out, &desc_out, (void*)bufOutput, &ciErrNum);

3.寻找当前循环的像素点.

uint4 pixel = read_imageui(sourceImage,a_sampler,(int2)(ix,iy));

unit4结构表明 该像素通道是4通道图像,参考逻辑代码中的配置信息可见其为BGRA通道图像:

cl_image_format format;
format.image_channel_order = CL_BGRA;
format.image_channel_data_type = CL_UNSIGNED_INT8;

4.第一步在输出图像中展开输入的原图到四角拼接的位置,即旋转移动到四角相接.

在这里插入图片描述

//code;
const int w_dest = (w+2*h);
//code;
write_imageui(outputImage,(int2)(h+ix,iy),pixel); //top
write_imageui(outputImage,(int2)(w_dest-(h+ix),w_dest-iy),pixel);//bottom
write_imageui(outputImage,(int2)(iy,w+h-ix),pixel);//left
write_imageui(outputImage,(int2)(w_dest-iy,h+ix),pixel);//right
  1. 确定top图像的输出位置信息,top图像中ix,iy为源图像的偏移量,只需要计算出在目标图像的偏移量即可绘制在目标图像中.其中X轴偏移了h(height)并以此为原点绘制,Y轴没有偏移.

  2. 确定bottom图像的输出位置,bottom图像是top图像的180°旋转后平移到输出图像底部.即偏移计算如同下图所示:其图像由右下角作为图像原点进行绘制,其绘制由下往上(w_dest-iy),由右向左(w_dest-(h+ix))绘制.

在这里插入图片描述

  1. 确定left图像的输出位置,left图像是top图像90°旋转后平移到输出图像左侧.其原始图像的x轴偏移现在变成了输出图像的Y轴偏移,即用iy替代掉ix,即源图的X轴分量变为了输出图像的Y轴分量.同时,输出图像的Y轴方向变成了w+h-ix的分量,且由下往上(同源图的由左向右)绘制.
    源 图 像 的 Y 轴 替 换 成 X 轴 的 偏 移 量 ( i n t 2 ) ( i x , i y ) − > 目 标 图 像 ( i n t 2 ) ( i y , w + h − i x ) 源图像的Y轴替换成X轴的偏移量(int2)(ix,iy)->目标图像(int2)(iy,w+h-ix) YX(int2)(ix,iy)>(int2)(iy,w+hix)
    在这里插入图片描述

  2. 确定right图像的输出位置,right图像是top图像-90°旋转后平移到输出图像右侧.其分析如上面第3点的分析类似,仅仅是坐标轴对换后的偏移量修正就行,下图给出修正结果.

在这里插入图片描述

5.第二步增加四个方向图像的偏移量,使得图像能够产生重叠区域.

//code;
const int mark_w = (int)(w/3);//横向偏移量
const int mark_h = (int)(h/3);//纵向偏移量 
//code;
//move to center
write_imageui(outputImage,(int2)(h+ix,iy+mark_w),pixel); //top
write_imageui(outputImage,(int2)(w_dest-(h+ix),w_dest-iy-mark_w),pixel);//bottom
write_imageui(outputImage,(int2)(iy+mark_h,w+h-ix),pixel);//left
write_imageui(outputImage,(int2)(w_dest-iy-mark_h,h+ix),pixel);//right

top和bottom分别向中心移动mark_w的距离(为了营造出中心灰度区域的长方形效果,如果四张图都移动mark_h或mark_w就是矩形),left和right分别向中心移动mark_h距离.

在这里插入图片描述

其中我们可以知道8个point的定义如下代码所示(这里需要注意计算都是按照原点[0,0]为基准计算):

//(int2) point1 = (int2)(h,h);
//(int2) ponit5 = (int2)(h + mark_h,h + mark_w);
//(int2) point2 = (int2)(h,h + w);
//(int2) point6 = (int2)(h + mark_h,h + w - mark_w);
//(int2) point3 = (int2)(h + w,h + w);
//(int2) point7 = (int2)(h + w - mark_h, h + w - mark_w);
//(int2) point4 = (int2)(h + w,h);
//(int2) point8 = (int2)(h + w - mark_h,h + mark_w);

由直线公式我们可以得出线段(15,26,37,48),而且结合部分的轮廓已近出来了.这个轮廓部分就是我们需要拼接的区域,其外沿(1,2,3,4)四个点构成,其内沿(5,6,7,8)四个点构成.由于四个图形成了重叠区域,我们将重叠区域按照线段分割,形成拼接效果如图:

在这里插入图片描述

由上图可以看到,我们需要将沿线段重叠区域的部分分割就可以形成拼接图形,下面先由top这张图示意分割过程:

//solution top	
//(int2) point1 = (int2)(h,h);
//(int2) ponit5 = (int2)(h + mark_h,h + mark_w);
//(int2) point4 = (int2)(h + w,h);
//(int2) point8 = (int2)(h + w - mark_h,h + mark_w);
if(iy>h-mark_w &&  iy+mark_w < ((h+ix)-h)*(h+mark_w-h)/(h+mark_h-h)+h){
	if( iy+mark_w >((h+ix)-(h+w))*((h+mark_w)-(h))/((h + w - mark_h)-(h + w))+h){
	}else{
		write_imageui(outputImage,(int2)(h+ix,iy+mark_w),pixel);//top
	}		
}

​ 1.上面的代码中,我们先来比较第一个if语句,其中条件:
i y &gt; h − m a r k _ w iy&gt;h-mark\_w iy>hmark_w
决定了我们是否只是截取拼接的有效区域,即源图像的重合部分图像高度.如果使用该条件那么如下图左,如果不使用该条件如下图右:

在这里插入图片描述

即我们是否只使用下面的区域:

在这里插入图片描述

​ 2.上面代码中,我们现在来看第一个if的第二个条件:
i y + m a r k _ w &lt; ( ( h + i x ) − h ) ∗ ( h + m a r k _ w − h ) / ( h + m a r k _ h − h ) + h iy+mark\_w &lt; ((h+ix)-h)*(h+mark\_w-h)/(h+mark\_h-h)+h iy+mark_w<((h+ix)h)(h+mark_wh)/(h+mark_hh)+h
这个条件看上去很复杂,其实就是直线公式的一个判断:
两 点 确 定 一 条 直 线 公 式 : ( y − y 1 ) / ( y 2 − y 1 ) = ( x − x 1 ) / ( x 2 − x 1 ) 两点确定一条直线公式:(y-y_1)/(y_2-y_1)=(x-x_1)/(x_2-x_1) 线:(yy1)/(y2y1)=(xx1)/(x2x1)
前面我们知道了point的位置坐标表示,分别将point1,point5带入直线公式,就可以得出第一个if的第二个条件.其实完成了下图的工作:

在这里插入图片描述

同理,第二个if语句的判断条件完成图形右半边的剪切工作:
i y + m a r k _ w &gt; ( ( h + i x ) − ( h + w ) ) ∗ ( ( h + m a r k _ w ) − ( h ) ) / ( ( h + w − m a r k _ h ) − ( h + w ) ) + h iy+mark\_w &gt;((h+ix)-(h+w))*((h+mark\_w)-(h))/((h + w - mark\_h)-(h + w))+h iy+mark_w>((h+ix)(h+w))((h+mark_w)(h))/((h+wmark_h)(h+w))+h
​ 3.同理,bottom部分我们也可以绘制出来,其判断条件我们可以不再书写,因为其本生图像就是一个top图像的镜像操作.当然,如果在top和bottom不是一个源图像的情况下也可以自行计算其if语句的约束条件:

在这里插入图片描述

其代码结构如下:

//solution bottom	
//real ues mirror of top

if(iy>h-mark_w &&  iy+mark_w < ((h+ix)-h)*(h+mark_w-h)/(h+mark_h-h)+h){
	if( iy+mark_w >((h+ix)-(h+w))*((h+mark_w)-(h))/((h + w - mark_h)-(h + w))+h){
	}else{
		write_imageui(outputImage,(int2)(w_dest-(h+ix),w_dest-(iy+mark_w)),pixel);//buttom
	}		
}

4.同理,left和right也可以照此分析:

	//solution left
	//(int2) point1 = (int2)(h,h);
	//(int2) ponit5 = (int2)(h + mark_h,h + mark_w);
	//(int2) point2 = (int2)(h,h + w);
	//(int2) point6 = (int2)(h + mark_h,h + w - mark_w);
	//-90 degree points 
	//(int2) point2 = (int2)(h,h);
	//(int2) point6 = (int2)(h+mark_w,h+mark_h);
	//(int2) point1 = (int2)(h+w,h);
	//(int2) point5 = (int2)(h+w-mark_w,h+mark_h)
	//参考坐标系的点位
	//(int2) point = (int2)(h+ix,iy+mark_h)

	if(iy>h-mark_h && iy+mark_h < ((h+ix)-h)*((h+mark_h)-h)/((h+mark_w)-h)+h){
		if( iy+mark_h >((h+ix)-(h+w))*((h+mark_h)-h)/((h+w-mark_w)-(h+w))+h){
		}else{
			write_imageui(outputImage,(int2)(iy+mark_h,w+h-ix),pixel);//left	
		}		
	}

先来看left的结构,我们注意到上述条件中有一个明显差异就是,if语句的第一个判定条件已经不同于top和bottom的条件:
t o p 的 条 件 : i y &gt; h − m a r k _ w ≠ l e f t 的 条 件 : i y &gt; h − m a r k _ h top的条件:iy&gt;h-mark\_w ≠left的条件:iy&gt;h-mark\_h top:iy>hmark_w̸=left:iy>hmark_h
这个原因在于,我们实际上每次判断时是对于源图像的循环(w,h)的循环.也就是说我们在写条件判断的时候需要将left图像右旋90°变成下图所示来进行条件判定.

在这里插入图片描述

同理,我们进行判断条件的x和y坐标也要转换为相应的-90°的坐标体系及:
参 考 坐 标 系 的 点 位 − &gt; ( i n t 2 ) p o i n t = ( i n t 2 ) ( h + i x , i y + m a r k _ h ) 参考坐标系的点位-&gt; (int2) point = (int2)(h+ix,iy+mark\_h) >(int2)point=(int2)(h+ix,iy+mark_h)
这样原本在旋转前的坐标体系转换为了旋转后的坐标体系:
( i n t 2 ) p o i n t 2 = ( i n t 2 ) ( h , h + w ) − &gt; ( i n t 2 ) p o i n t 2 = ( i n t 2 ) ( h , h ) (int2) point2 = (int2)(h,h + w)-&gt;(int2) point2 = (int2)(h,h) (int2)point2=(int2)(h,h+w)>(int2)point2=(int2)(h,h)

( i n t 2 ) p o i n t 6 = ( i n t 2 ) ( h + m a r k _ h , h + w − m a r k _ w ) − &gt; ( i n t 2 ) p o i n t 6 = ( i n t 2 ) ( h + m a r k _ w , h + m a r k _ h ) (int2) point6 = (int2)(h + mark\_h,h + w - mark\_w)-&gt;(int2) point6 = (int2)(h+mark\_w,h+mark\_h) (int2)point6=(int2)(h+mark_h,h+wmark_w)>(int2)point6=(int2)(h+mark_w,h+mark_h)

( i n t 2 ) p o i n t 1 = ( i n t 2 ) ( h , h ) − &gt; ( i n t 2 ) p o i n t 1 = ( i n t 2 ) ( h + w , h ) (int2) point1 = (int2)(h,h) -&gt; (int2) point1 = (int2)(h+w,h) (int2)point1=(int2)(h,h)>(int2)point1=(int2)(h+w,h)

( i n t 2 ) p o n i t 5 = ( i n t 2 ) ( h + m a r k _ h , h + m a r k _ w ) − &gt; ( i n t 2 ) p o i n t 5 = ( i n t 2 ) ( h + w − m a r k _ w , h + m a r k _ h ) (int2) ponit5 = (int2)(h + mark\_h,h + mark\_w) -&gt;(int2) point5 = (int2)(h+w-mark\_w,h+mark\_h) (int2)ponit5=(int2)(h+mark_h,h+mark_w)>(int2)point5=(int2)(h+wmark_w,h+mark_h)

由此看出,我们只要按照原有的top的条件结构,以转换后的坐标体系构建left的if条件就能够得到left的图像,同理right图像是left图像的mirror计算.

//solution right
//mirror of left
	
if(iy>h-mark_h && iy+mark_h < ((h+ix)-h)*((h+mark_h)-h)/((h+mark_w)-h)+h){
	if( iy+mark_h >((h+ix)-(h+w))*((h+mark_h)-h)/((h+w-mark_w)-(h+w))+h){
	}else{
		write_imageui(outputImage,(int2)(w_dest-iy-mark_h,h+ix),pixel);//right
	}		
}

这个我们可以看到在实际的绘制中,write_imageui函数调用的参数并不是旋转以后的新坐标系参数,这个原因是因为,我们最后出来的目标输出图像其结构(坐标系)和top图像是一致的,而非旋转以后的新坐标系,因此,需要采用和top一致的坐标结构即:
( i n t 2 ) p o i n t = ( i n t 2 ) ( h + i x , i y + m a r k _ h ) 对 应 旋 转 后 的 坐 标 结 构 (int2) point = (int2)(h+ix,iy+mark\_h)对应旋转后的坐标结构 (int2)point=(int2)(h+ix,iy+mark_h)

( i n t 2 ) ( i y + m a r k _ h , w + h − i x ) 对 应 旋 转 前 和 t o p 图 像 一 致 的 图 像 结 构 (int2)(iy+mark\_h,w+h-ix)对应旋转前和top图像一致的图像结构 (int2)(iy+mark_h,w+hix)top

6.我们在上面的代码中可以看到,我们对于内存的使用其实是非常大的,在实际中我们分配给3个部分的内存,这个部分的最后我们讨论如何最小化的使用内存:

(基于代码kernel6.cl,opencl_test03.cpp)

  1. 输出图像

    cl_mem          memOutputBuffer = NULL;             // 输出内存对象
    
    cl_image_format format_out;
    format_out.image_channel_order = CL_BGRA;
    format_out.image_channel_data_type = CL_UNSIGNED_INT8;
    
    cl_image_desc desc_out;
    desc_out.image_type = CL_MEM_OBJECT_IMAGE2D;
    desc_out.image_width = width + 2 * height;
    desc_out.image_height = width + 2 * height;
    desc_out.image_depth = 0;
    desc_out.image_array_size = 0;
    desc_out.image_row_pitch = 0;
    desc_out.image_slice_pitch = 0;
    desc_out.num_mip_levels = 0;
    desc_out.num_samples = 0;
    desc_out.buffer = NULL;
    
    memOutputBuffer = clCreateImage(Context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, &format_out, &desc_out, (void*)bufOutput, &ciErrNum);
    
    
  2. 用于拷贝的输出内存(复制image2d的有效图像数据)

    uchar           *bufOutput = NULL;
    //allocate the output buffer to store the image rotated.  
    
    if (NULL == (bufOutput = (unsigned char *)malloc((width + 2 * height)*(width + 2 * height) * 4 * sizeof(unsigned char)))) {
    		std::cerr << "Failed to malloc buffer for output image. " << std::endl;
    		return NULL;
    	}
    
    ciErrNum = clEnqueueReadImage(
    		CommandQueue,
    		memOutputBuffer,
    		CL_TRUE,
    		origin,
    		region,
    		0,
    		0,
    		bufOutput,
    		0,
    		NULL,
    		NULL);
    	if (ciErrNum != CL_SUCCESS) {
    		std::cerr << "Failed to read the image from the device." << std::endl;
    		return -1;
    	}
    
    
    
  3. 用于显示的mat结构

    Mat M((width + 2 * height), (width + 2 * height), CV_8UC4);
    cout << "M.channels : " << M.channels() << endl;
    memcpy(M.data, bufOutput, (width + 2 * height)* (width + 2 * height) * 4 * sizeof(unsigned char));
    //show the gray image rotated.  
    const char *winName = "rotated";
    namedWindow(winName, WINDOW_AUTOSIZE);
    imshow(winName, M);
    waitKey(0);
    destroyAllWindows();
    
    

    在上述代码段中我们可以看到这个三个地方的实际使用内存大小都被定义为;
    ( w i d t h + 2 ∗ h e i g h t ) ∗ ( w i d t h + 2 ∗ h e i g h t ) ∗ 4 ∗ s i z e o f ( u n s i g n e d c h a r ) (width + 2 * height)* (width + 2 * height) * 4 * sizeof(unsigned char) (width+2height)(width+2height)4sizeof(unsignedchar)
    这个是为了能够显示出完全的拼接图像区域,也就是包含已经偏移后的有效数据和没有偏移的拼接区域.如下图所示:

    在这里插入图片描述

    这个分配模式导致了巨大的系统开销(内存浪费),而实际上我们只需要中间的实际拼接区域大小的内存作为输出内存,理论上就能够满足输出需要了,那么我们下面讨论如何使用一块没有浪费的内存来进行输出.

    1. 首先,我们需要确定内存大小,由上面的结构我们可以看出其实需要使用的内存大小只需要满足:
      b u f O u t p u t = ( u n s i g n e d c h a r ∗ ) m a l l o c ( w i d t h ∗ w i d t h ∗ 4 ∗ s i z e o f ( u n s i g n e d c h a r ) ) bufOutput = (unsigned char *)malloc(width*width * 4 * sizeof(unsigned char)) bufOutput=(unsignedchar)malloc(widthwidth4sizeof(unsignedchar))
      也就是上面提到的三个部分内存,实际上都可以只分配这样的大小就可以了,这个内存区域就是完全没有任何浪费的内存大小.

    2. 回到我们前面提到的kernel编程,可能还有记忆其kernel结构如下:

      //solution top	
      //(int2) point1 = (int2)(h,h);
      //(int2) ponit5 = (int2)(h + mark_h,h + mark_w);
      //(int2) point4 = (int2)(h + w,h);
      //(int2) point8 = (int2)(h + w - mark_h,h + mark_w);
      
      if(iy>h-mark_w &&  iy+mark_w < ((h+ix)-h)*(h+mark_w-h)/(h+mark_h-h)+h){
      	if( iy+mark_w >((h+ix)-(h+w))*((h+mark_w)-(h))/((h + w - mark_h)-(h + w))+h)	{
      	}else{
      		write_imageui(outputImage,(int2)(h+ix,iy+mark_w),pixel);//top
      	}		
      }
      
      //solution bottom	
      //real ues mirror of top
      
      if(iy>h-mark_w &&  iy+mark_w < ((h+ix)-h)*(h+mark_w-h)/(h+mark_h-h)+h){
      	if( iy+mark_w >((h+ix)-(h+w))*((h+mark_w)-(h))/((h + w - mark_h)-(h + w))+h)	{
      	}else{
      		write_imageui(outputImage,(int2)(w_dest-(h+ix),w_dest-							(iy+mark_w)),pixel);//buttom
      		}		
      }
      
      //solution left
      //(int2) point1 = (int2)(h,h);
      //(int2) ponit5 = (int2)(h + mark_h,h + mark_w);
      //(int2) point2 = (int2)(h,h + w);
      //(int2) point6 = (int2)(h + mark_h,h + w - mark_w);
      //-90 degree points 
      //(int2) point2 = (int2)(h,h);
      //(int2) point6 = (int2)(h+mark_w,h+mark_h);
      //(int2) point1 = (int2)(h+w,h);
      //(int2) point5 = (int2)(h+w-mark_w,h+mark_h);
      //参考坐标系的点位
      //(int2) point = (int2)(h+ix,iy+mark_h)
      
      if(iy>h-mark_h && iy+mark_h < ((h+ix)-h)*((h+mark_h)-h)/((h+mark_w)-h)+h){
      	if( iy+mark_h >((h+ix)-(h+w))*((h+mark_h)-h)/((h+w-mark_w)-(h+w))+h){
      	}else{
      		write_imageui(outputImage,(int2)(iy+mark_h,w+h-ix),pixel);//left	
      	}		
      }
      	
      //solution right
      //mirror of left
      	
      if(iy>h-mark_h && iy+mark_h < ((h+ix)-h)*((h+mark_h)-h)/((h+mark_w)-h)+h){
      	if( iy+mark_h >((h+ix)-(h+w))*((h+mark_h)-h)/((h+w-mark_w)-(h+w))+h){
      	}else{
      		write_imageui(outputImage,(int2)(w_dest-iy-mark_h,h+ix),pixel);//right
      	}		
      }
      
      

      前面的结构中我们详细讨论了两层if语句中的逻辑结构,现在由于要考虑输出buff部分的不浪费,其实我们需要考虑的是 write_imageui函数在什么地方进行图像绘制.

      1. 以top为例:

        在这里插入图片描述

        我们是否还记得这张图像,在if的第一个条件中我们判断了图像截取的有效区域需要是:
        i y &gt; h − m a r k _ h iy&gt;h-mark\_h iy>hmark_h
        也就是说,绘制输出图像的时候我们第一个点的选取是iy=h-mark_h,下面我们来区分下面的两个式子:

        . //write_imageui(outputImage,(int2)(h+ix,iy+mark_w),pixel);//top. write_imageui(outputImage,(int2)(ix,iy-(h-mark_w)),pixel);//top
        
        

        其中一式是以整个图像组合大小计算偏移量的图像函数,其偏移的计算是由上图左上的图像区域计算.而二式是由上图右上的有效区域为偏移计算的.以有效区域进行计算的时候其实更加像是抠出图像中的一块来进行填充大过图像偏移.由于是填充所以x不需要修正即为ix,而y需要修正为需要的区域及iy-(h-mark_w).

      2. 同理,bottom区域也做同样的修正即可,因为是top的镜像,所以使用w进行镜像操作:

        . //write_imageui(outputImage,(int2)(w_dest-(h+ix),w_dest-						(iy+mark_w)),pixel);//buttom. write_imageui(outputImage,(int2)(ix,w-(iy-(h-mark_w))),pixel);//buttom
        
        
      3. 对于left结构,我们上面讨论过其结构存在一个x与y的翻转关系,也就是-90°的旋转.所以其操作如下:

        . //write_imageui(outputImage,(int2)(iy+mark_h,w+h-ix),pixel);//left. write_imageui(outputImage,(int2)(iy-(h-mark_h),ix),pixel);//left	
        
        
      4. 对于right结构,我们又可以看做是left的镜像:

        . //write_imageui(outputImage,(int2)(w_dest-iy-mark_h,h+ix),pixel);//right. write_imageui(outputImage,(int2)(w-(iy-(h-mark_h)),ix),pixel);//right
        
        
    3. 到此为止,我们优化了内存的结构,使得分配的内存达到最小,也就是输出的图像最优.

      cl_image_format format_out;
      format_out.image_channel_order = CL_BGRA;
      format_out.image_channel_data_type = CL_UNSIGNED_INT8;
      
      cl_image_desc desc_out;
      desc_out.image_type = CL_MEM_OBJECT_IMAGE2D;
      desc_out.image_width = width;
      desc_out.image_height = width;
      desc_out.image_depth = 0;
      desc_out.image_array_size = 0;
      desc_out.image_row_pitch = 0;
      desc_out.image_slice_pitch = 0;
      desc_out.num_mip_levels = 0;
      desc_out.num_samples = 0;
      desc_out.buffer = NULL;
      
      //allocate the output buffer to store the image rotated.  
      if (NULL == (bufOutput = (unsigned char *)malloc(width*width * 4 * 					sizeof(unsigned char)))) {
      	std::cerr << "Failed to malloc buffer for output image. " << std::endl;
      	return NULL;
      }
      //memset(buffer,0,(width + 2 * height) * (width + 2 * height) * 4);
      memOutputBuffer = clCreateImage(Context, CL_MEM_READ_ONLY | 						CL_MEM_COPY_HOST_PTR, &format_out, &desc_out, (void*)bufOutput, &ciErrNum);
      
      

在这里插入图片描述

以下是四幅图片输入的环视拼接 :

基于代码kernel421.cl,opencl_4img2one.cpp

在这里插入图片描述

四张输入图片的处理,我们为了在一个kernel中处理四张图片,将输入的图片调整到大小一致(考虑到kernel执行的便利程度).

memInputBuffer_top = clCreateImage(Context, CL_MEM_WRITE_ONLY | CL_MEM_COPY_HOST_PTR, &format, &desc, (void *)img_top.data, &ciErrNum);//top

memInputBuffer_right= clCreateImage(Context, CL_MEM_WRITE_ONLY | CL_MEM_COPY_HOST_PTR, &format, &desc, (void *)img_right.data, &ciErrNum);//right

memInputBuffer_bottom = clCreateImage(Context, CL_MEM_WRITE_ONLY | CL_MEM_COPY_HOST_PTR, &format, &desc, (void *)img_bottom.data, &ciErrNum);//bottom

memInputBuffer_left = clCreateImage(Context, CL_MEM_WRITE_ONLY | CL_MEM_COPY_HOST_PTR, &format, &desc, (void *)img_left.data, &ciErrNum);//right

//code;

//set the kernel arguments  
clSetKernelArg(Kernel, 0, sizeof(cl_mem), (void *)&memInputBuffer_top);
clSetKernelArg(Kernel, 1, sizeof(cl_mem), (void *)&memInputBuffer_right);
clSetKernelArg(Kernel, 2, sizeof(cl_mem), (void *)&memInputBuffer_bottom);
clSetKernelArg(Kernel, 3, sizeof(cl_mem), (void *)&memInputBuffer_left);
clSetKernelArg(Kernel, 4, sizeof(cl_mem), (void *)&memOutputBuffer);
clSetKernelArg(Kernel, 5, sizeof(cl_int), (void *)&width);
clSetKernelArg(Kernel, 6, sizeof(cl_int), (void *)&height);

我们修改main函数中和kernel中的的参数以便于传入四张图像:

__kernel void img_rotate(
	__read_only image2d_t sourceImage_top,
	__read_only image2d_t sourceImage_right,
	__read_only image2d_t sourceImage_bottom,
	__read_only image2d_t sourceImage_left,
    __write_only image2d_t outputImage,
	int w,
	int h){
	//code;
	uint4 pixel_top = read_imageui(sourceImage_top,a_sampler,(int2)(ix,iy));
	uint4 pixel_right = read_imageui(sourceImage_right,a_sampler,(int2)(ix,iy));
	uint4 pixel_bottom = read_imageui(sourceImage_bottom,a_sampler,(int2)(ix,iy));
	uint4 pixel_left = read_imageui(sourceImage_left,a_sampler,(int2)(ix,iy));
	//code;
	}
	

这里需要注意的是,每幅输入图片都是依照没有旋转过的结构(宽高)来进行数据读取,而按照旋转过的位置进行填充(针对输出图像),所以我们的输出填充部分可以***完全按照前面(单一输入的4格环视拼接)部分来编写*** ,唯一的区别就是填充的数据要用pixel_x来表示:

//solution top	
//(int2) point1 = (int2)(h,h);
//(int2) ponit5 = (int2)(h + mark_h,h + mark_w);
//(int2) point4 = (int2)(h + w,h);
//(int2) point8 = (int2)(h + w - mark_h,h + mark_w);

if( iy+mark_w < ((h+ix)-h)*(h+mark_w-h)/(h+mark_h-h)+h){
	if( iy+mark_w >((h+ix)-(h+w))*((h+mark_w)-(h))/((h + w - mark_h)-(h + w))+h){
	}else{
		write_imageui(outputImage,(int2)(h+ix,iy+mark_w),pixel_top);//top
	}		
}

//solution bottom	
//real ues mirror of top

if(iy>h-mark_w &&  iy+mark_w < ((h+ix)-h)*(h+mark_w-h)/(h+mark_h-h)+h){
	if( iy+mark_w >((h+ix)-(h+w))*((h+mark_w)-(h))/((h + w - mark_h)-(h + w))+h){
	}else{
		write_imageui(outputImage,(int2)(w_dest-(h+ix),w_dest-								(iy+mark_w)),pixel_bottom);//buttom
	}		
}

//solution left
//(int2) point1 = (int2)(h,h);
//(int2) ponit5 = (int2)(h + mark_h,h + mark_w);
//(int2) point2 = (int2)(h,h + w);
//(int2) point6 = (int2)(h + mark_h,h + w - mark_w);
//-90 degree points 
//(int2) point2 = (int2)(h,h);
//(int2) point6 = (int2)(h+mark_w,h+mark_h);
//(int2) point1 = (int2)(h+w,h);
//(int2) point5 = (int2)(h+w-mark_w,h+mark_h);
//参考坐标系的点位
//(int2) point = (int2)(h+ix,iy+mark_h)

if(iy>h-mark_h && iy+mark_h < ((h+ix)-h)*((h+mark_h)-h)/((h+mark_w)-h)+h){
	if( iy+mark_h >((h+ix)-(h+w))*((h+mark_h)-h)/((h+w-mark_w)-(h+w))+h){
	}else{
		write_imageui(outputImage,(int2)(iy+mark_h,w+h-ix),pixel_left);//left	
	}		
}
	
//solution right
//mirror of left
	
if(iy>h-mark_h && iy+mark_h < ((h+ix)-h)*((h+mark_h)-h)/((h+mark_w)-h)+h){
	if( iy+mark_h >((h+ix)-(h+w))*((h+mark_h)-h)/((h+w-mark_w)-(h+w))+h){
	}else{
		write_imageui(outputImage,(int2)(w_dest-iy-mark_h,h+ix),pixel_right);//right
	}		
}

由此完成, 421的360°环视拼接:

在这里插入图片描述

在这里插入图片描述

文档和完整代码下载

end! thanks!

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值