fix a crash on CPU OCL for some specified image sizes

//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
//    Niko Li, newlife20080214@gmail.com
//    Wang Weiyan, wangweiyanster@gmail.com
//    Jia Haipeng, jiahaipeng95@gmail.com
//    Nathan, liujun@multicorewareinc.com
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other oclMaterials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors as is and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//

#pragma OPENCL EXTENSION cl_amd_printf : enable
#define CV_HAAR_FEATURE_MAX           3

#define calc_sum(rect,offset)        (sum[(rect).p0+offset] - sum[(rect).p1+offset] - sum[(rect).p2+offset] + sum[(rect).p3+offset])
#define calc_sum1(rect,offset,i)     (sum[(rect).p0[i]+offset] - sum[(rect).p1[i]+offset] - sum[(rect).p2[i]+offset] + sum[(rect).p3[i]+offset])

typedef int   sumtype;
typedef float sqsumtype;

typedef struct  __attribute__((aligned (128)))  GpuHidHaarFeature
{
    struct __attribute__((aligned (32)))
{
    int p0 __attribute__((aligned (4)));
    int p1 __attribute__((aligned (4)));
    int p2 __attribute__((aligned (4)));
    int p3 __attribute__((aligned (4)));
    float weight __attribute__((aligned (4)));
}
rect[CV_HAAR_FEATURE_MAX] __attribute__((aligned (32)));
}
GpuHidHaarFeature;

typedef struct __attribute__((aligned (128) )) GpuHidHaarTreeNode
{
    int p[CV_HAAR_FEATURE_MAX][4] __attribute__((aligned (64)));
    float weight[CV_HAAR_FEATURE_MAX] /*__attribute__((aligned (16)))*/;
    float threshold /*__attribute__((aligned (4)))*/;
    float alpha[2] __attribute__((aligned (8)));
    int left __attribute__((aligned (4)));
    int right __attribute__((aligned (4)));
}
GpuHidHaarTreeNode;

typedef struct __attribute__((aligned (32))) GpuHidHaarClassifier
{
    int count __attribute__((aligned (4)));
    GpuHidHaarTreeNode* node __attribute__((aligned (8)));
    float* alpha __attribute__((aligned (8)));
}
GpuHidHaarClassifier;

typedef struct __attribute__((aligned (64))) GpuHidHaarStageClassifier
{
    int  count __attribute__((aligned (4)));
    float threshold __attribute__((aligned (4)));
    int two_rects __attribute__((aligned (4)));
    int reserved0 __attribute__((aligned (8)));
    int reserved1 __attribute__((aligned (8)));
    int reserved2 __attribute__((aligned (8)));
    int reserved3 __attribute__((aligned (8)));
}
GpuHidHaarStageClassifier;

typedef struct __attribute__((aligned (64))) GpuHidHaarClassifierCascade
{
    int  count __attribute__((aligned (4)));
    int  is_stump_based __attribute__((aligned (4)));
    int  has_tilted_features __attribute__((aligned (4)));
    int  is_tree __attribute__((aligned (4)));
    int pq0 __attribute__((aligned (4)));
    int pq1 __attribute__((aligned (4)));
    int pq2 __attribute__((aligned (4)));
    int pq3 __attribute__((aligned (4)));
    int p0 __attribute__((aligned (4)));
    int p1 __attribute__((aligned (4)));
    int p2 __attribute__((aligned (4)));
    int p3 __attribute__((aligned (4)));
    float inv_window_area __attribute__((aligned (4)));
} GpuHidHaarClassifierCascade;

__kernel void __attribute__((reqd_work_group_size(8,8,1)))gpuRunHaarClassifierCascade(
    global GpuHidHaarStageClassifier * stagecascadeptr,
    global int4 * info,
    global GpuHidHaarTreeNode * nodeptr,
    global const int * restrict sum1,
    global const float * restrict sqsum1,
    global int4 * candidate,
    const int pixelstep,
    const int loopcount,
    const int start_stage,
    const int split_stage,
    const int end_stage,
    const int startnode,
    const int splitnode,
    const int4 p,
    const int4 pq,
    const float correction)
{
    int grpszx = get_local_size(0);
    int grpszy = get_local_size(1);
    int grpnumx = get_num_groups(0);
    int grpidx = get_group_id(0);
    int lclidx = get_local_id(0);
    int lclidy = get_local_id(1);

    int lcl_sz = mul24(grpszx,grpszy);
    int lcl_id = mad24(lclidy,grpszx,lclidx);

    __local int lclshare[1024];
    __local int* lcldata = lclshare;//for save win data
    __local int* glboutindex = lcldata + 28*28;//for save global out index
    __local int* lclcount = glboutindex + 1;//for save the numuber of temp pass pixel
    __local int* lcloutindex = lclcount + 1;//for save info of temp pass pixel
    __local float* partialsum = (__local float*)(lcloutindex + (lcl_sz<<1));
    glboutindex[0]=0;
    int outputoff = mul24(grpidx,256);

    //assume window size is 20X20
#define WINDOWSIZE 20+1
    //make sure readwidth is the multiple of 4
    //ystep =1, from host code
    int readwidth = ((grpszx-1 + WINDOWSIZE+3)>>2)<<2;
    int readheight = grpszy-1+WINDOWSIZE;
    int read_horiz_cnt = readwidth >> 2;//each read int4
    int total_read = mul24(read_horiz_cnt,readheight);
    int read_loop = (total_read + lcl_sz - 1) >> 6;
    candidate[outputoff+(lcl_id<<2)] = (int4)0;
    candidate[outputoff+(lcl_id<<2)+1] = (int4)0;
    candidate[outputoff+(lcl_id<<2)+2] = (int4)0;
    candidate[outputoff+(lcl_id<<2)+3] = (int4)0;
    for(int scalei = 0; scalei <loopcount; scalei++)
    {
        int4 scaleinfo1= info[scalei];
        int width = (scaleinfo1.x & 0xffff0000) >> 16;
        int height = scaleinfo1.x & 0xffff;
        int grpnumperline =(scaleinfo1.y & 0xffff0000) >> 16;
        int totalgrp = scaleinfo1.y & 0xffff;
        int imgoff = scaleinfo1.z;
        float factor = as_float(scaleinfo1.w);

        __global const int * sum = sum1 + imgoff;
        __global const float * sqsum = sqsum1 + imgoff;
        for(int grploop=grpidx; grploop<totalgrp; grploop+=grpnumx)
        {
            int grpidy = grploop / grpnumperline;
            int grpidx = grploop - mul24(grpidy, grpnumperline);
            int x = mad24(grpidx,grpszx,lclidx);
            int y = mad24(grpidy,grpszy,lclidy);
            int grpoffx = x-lclidx;
            int grpoffy = y-lclidy;

            for(int i=0; i<read_loop; i++)
            {
                int pos_id = mad24(i,lcl_sz,lcl_id);
                pos_id = pos_id < total_read ? pos_id : 0;

                int lcl_y = pos_id / read_horiz_cnt;
                int lcl_x = pos_id - mul24(lcl_y, read_horiz_cnt);

                int glb_x = grpoffx + (lcl_x<<2);
                int glb_y = grpoffy + lcl_y;

                int glb_off = mad24(min(glb_y, height - 1),pixelstep,glb_x);
                int4 data = *(__global int4*)&sum[glb_off];
                int lcl_off = mad24(lcl_y, readwidth, lcl_x<<2);

                vstore4(data, 0, &lcldata[lcl_off]);
            }

            lcloutindex[lcl_id] = 0;
            lclcount[0] = 0;
            int result = 1;
            int nodecounter= startnode;
            float mean, variance_norm_factor;
            barrier(CLK_LOCAL_MEM_FENCE);

            int lcl_off = mad24(lclidy,readwidth,lclidx);
            int4 cascadeinfo1, cascadeinfo2;
            cascadeinfo1 = p;
            cascadeinfo2 = pq;

            cascadeinfo1.x +=lcl_off;
            cascadeinfo1.z +=lcl_off;
            mean = (lcldata[mad24(cascadeinfo1.y,readwidth,cascadeinfo1.x)] - lcldata[mad24(cascadeinfo1.y,readwidth,cascadeinfo1.z)] -
                    lcldata[mad24(cascadeinfo1.w,readwidth,cascadeinfo1.x)] + lcldata[mad24(cascadeinfo1.w,readwidth,cascadeinfo1.z)])
                    *correction;

            int p_offset = mad24(y, pixelstep, x);

            cascadeinfo2.x +=p_offset;
            cascadeinfo2.z +=p_offset;
            variance_norm_factor =sqsum[mad24(cascadeinfo2.y, pixelstep, cascadeinfo2.x)] - sqsum[mad24(cascadeinfo2.y, pixelstep, cascadeinfo2.z)] -
                                    sqsum[mad24(cascadeinfo2.w, pixelstep, cascadeinfo2.x)] + sqsum[mad24(cascadeinfo2.w, pixelstep, cascadeinfo2.z)];

            variance_norm_factor = variance_norm_factor * correction - mean * mean;
            variance_norm_factor = variance_norm_factor >=0.f ? sqrt(variance_norm_factor) : 1.f;

            for(int stageloop = start_stage; (stageloop < split_stage)  && result; stageloop++ )
            {
                float stage_sum = 0.f;
                int2 stageinfo = *(global int2*)(stagecascadeptr+stageloop);
                float stagethreshold = as_float(stageinfo.y);
                for(int nodeloop = 0; nodeloop < stageinfo.x; nodeloop++ )
                {
                    __global GpuHidHaarTreeNode* currentnodeptr = (nodeptr + nodecounter);

                    int4 info1 = *(__global int4*)(&(currentnodeptr->p[0][0]));
                    int4 info2 = *(__global int4*)(&(currentnodeptr->p[1][0]));
                    int4 info3 = *(__global int4*)(&(currentnodeptr->p[2][0]));
                    float4 w = *(__global float4*)(&(currentnodeptr->weight[0]));
                    float2 alpha2 = *(__global float2*)(&(currentnodeptr->alpha[0]));
                    float nodethreshold  = w.w * variance_norm_factor;

                    info1.x +=lcl_off;
                    info1.z +=lcl_off;
                    info2.x +=lcl_off;
                    info2.z +=lcl_off;

                    float classsum = (lcldata[mad24(info1.y,readwidth,info1.x)] - lcldata[mad24(info1.y,readwidth,info1.z)] -
                                        lcldata[mad24(info1.w,readwidth,info1.x)] + lcldata[mad24(info1.w,readwidth,info1.z)]) * w.x;

                    classsum += (lcldata[mad24(info2.y,readwidth,info2.x)] - lcldata[mad24(info2.y,readwidth,info2.z)] -
                                    lcldata[mad24(info2.w,readwidth,info2.x)] + lcldata[mad24(info2.w,readwidth,info2.z)]) * w.y;

                    info3.x +=lcl_off;
                    info3.z +=lcl_off;
                    classsum += (lcldata[mad24(info3.y,readwidth,info3.x)] - lcldata[mad24(info3.y,readwidth,info3.z)] -
                                    lcldata[mad24(info3.w,readwidth,info3.x)] + lcldata[mad24(info3.w,readwidth,info3.z)]) * w.z;

                    stage_sum += classsum >= nodethreshold ? alpha2.y : alpha2.x;
                    nodecounter++;
                }

                result = (stage_sum >= stagethreshold);
            }

            if(result && (x < width) && (y < height))
            {
                int queueindex = atomic_inc(lclcount);
                lcloutindex[queueindex<<1] = (lclidy << 16) | lclidx;
                lcloutindex[(queueindex<<1)+1] = as_int(variance_norm_factor);
            }
            barrier(CLK_LOCAL_MEM_FENCE);
            int queuecount  = lclcount[0];
            barrier(CLK_LOCAL_MEM_FENCE);
            nodecounter = splitnode;
            for(int stageloop = split_stage; stageloop< end_stage && queuecount>0; stageloop++)
            {
                lclcount[0]=0;
                barrier(CLK_LOCAL_MEM_FENCE);

                int2 stageinfo = *(global int2*)(stagecascadeptr+stageloop);
                float stagethreshold = as_float(stageinfo.y);

                int perfscale = queuecount > 4 ? 3 : 2;
                int queuecount_loop = (queuecount + (1<<perfscale)-1) >> perfscale;
                int lcl_compute_win = lcl_sz >> perfscale;
                int lcl_compute_win_id = (lcl_id >>(6-perfscale));
                int lcl_loops = (stageinfo.x + lcl_compute_win -1) >> (6-perfscale);
                int lcl_compute_id = lcl_id - (lcl_compute_win_id << (6-perfscale));
                for(int queueloop=0; queueloop<queuecount_loop; queueloop++)
                {
                    float stage_sum = 0.f;
                    int temp_coord = lcloutindex[lcl_compute_win_id<<1];
                    float variance_norm_factor = as_float(lcloutindex[(lcl_compute_win_id<<1)+1]);
                    int queue_pixel = mad24(((temp_coord  & (int)0xffff0000)>>16),readwidth,temp_coord & 0xffff);

                    if(lcl_compute_win_id < queuecount)
                    {

                        int tempnodecounter = lcl_compute_id;
                        float part_sum = 0.f;
                        for(int lcl_loop=0; lcl_loop<lcl_loops && tempnodecounter<stageinfo.x; lcl_loop++)
                        {
                            __global GpuHidHaarTreeNode* currentnodeptr = (nodeptr + nodecounter + tempnodecounter);

                            int4 info1 = *(__global int4*)(&(currentnodeptr->p[0][0]));
                            int4 info2 = *(__global int4*)(&(currentnodeptr->p[1][0]));
                            int4 info3 = *(__global int4*)(&(currentnodeptr->p[2][0]));
                            float4 w = *(__global float4*)(&(currentnodeptr->weight[0]));
                            float2 alpha2 = *(__global float2*)(&(currentnodeptr->alpha[0]));
                            float nodethreshold  = w.w * variance_norm_factor;

                            info1.x +=queue_pixel;
                            info1.z +=queue_pixel;
                            info2.x +=queue_pixel;
                            info2.z +=queue_pixel;

                            float classsum = (lcldata[mad24(info1.y,readwidth,info1.x)] - lcldata[mad24(info1.y,readwidth,info1.z)] -
                                                lcldata[mad24(info1.w,readwidth,info1.x)] + lcldata[mad24(info1.w,readwidth,info1.z)]) * w.x;

                            classsum += (lcldata[mad24(info2.y,readwidth,info2.x)] - lcldata[mad24(info2.y,readwidth,info2.z)] -
                                            lcldata[mad24(info2.w,readwidth,info2.x)] + lcldata[mad24(info2.w,readwidth,info2.z)]) * w.y;

                            info3.x +=queue_pixel;
                            info3.z +=queue_pixel;
                            classsum += (lcldata[mad24(info3.y,readwidth,info3.x)] - lcldata[mad24(info3.y,readwidth,info3.z)] -
                                            lcldata[mad24(info3.w,readwidth,info3.x)] + lcldata[mad24(info3.w,readwidth,info3.z)]) * w.z;

                            part_sum += classsum >= nodethreshold ? alpha2.y : alpha2.x;
                            tempnodecounter +=lcl_compute_win;
                        }//end for(int lcl_loop=0;lcl_loop<lcl_loops;lcl_loop++)
                        partialsum[lcl_id]=part_sum;
                    }
                    barrier(CLK_LOCAL_MEM_FENCE);
                    if(lcl_compute_win_id < queuecount)
                    {
                        for(int i=0; i<lcl_compute_win && (lcl_compute_id==0); i++)
                        {
                            stage_sum += partialsum[lcl_id+i];
                        }
                        if(stage_sum >= stagethreshold && (lcl_compute_id==0))
                        {
                            int queueindex = atomic_inc(lclcount);
                            lcloutindex[queueindex<<1] = temp_coord;
                            lcloutindex[(queueindex<<1)+1] = as_int(variance_norm_factor);
                        }
                        lcl_compute_win_id +=(1<<perfscale);
                    }
                    barrier(CLK_LOCAL_MEM_FENCE);
                }//end for(int queueloop=0;queueloop<queuecount_loop;queueloop++)

                queuecount = lclcount[0];
                barrier(CLK_LOCAL_MEM_FENCE);
                nodecounter += stageinfo.x;
            }//end for(int stageloop = splitstage; stageloop< endstage && queuecount>0;stageloop++)

            if(lcl_id<queuecount)
            {
                int temp = lcloutindex[lcl_id<<1];
                int x = mad24(grpidx,grpszx,temp & 0xffff);
                int y = mad24(grpidy,grpszy,((temp & (int)0xffff0000) >> 16));
                temp = glboutindex[0];
                int4 candidate_result;
                candidate_result.zw = (int2)convert_int_rtn(factor*20.f);
                candidate_result.x = convert_int_rtn(x*factor);
                candidate_result.y = convert_int_rtn(y*factor);
                atomic_inc(glboutindex);
                candidate[outputoff+temp+lcl_id] = candidate_result;
            }
            barrier(CLK_LOCAL_MEM_FENCE);
        }//end for(int grploop=grpidx;grploop<totalgrp;grploop+=grpnumx)
    }//end for(int scalei = 0; scalei <loopcount; scalei++)
}

/*
if(stagecascade->two_rects)
{
    #pragma unroll
    for( n = 0; n < stagecascade->count; n++ )
    {
        t1 = *(node + counter);
        t = t1.threshold * variance_norm_factor;
        classsum = calc_sum1(t1,p_offset,0) * t1.weight[0];

        classsum  += calc_sum1(t1, p_offset,1) * t1.weight[1];
        stage_sum += classsum >= t ? t1.alpha[1]:t1.alpha[0];

        counter++;
    }
}
else
{
    #pragma unroll
    for( n = 0; n < stagecascade->count; n++ )
    {
        t = node[counter].threshold*variance_norm_factor;
        classsum = calc_sum1(node[counter],p_offset,0) * node[counter].weight[0];
        classsum += calc_sum1(node[counter],p_offset,1) * node[counter].weight[1];

        if( node[counter].p0[2] )
            classsum += calc_sum1(node[counter],p_offset,2) * node[counter].weight[2];

        stage_sum += classsum >= t ? node[counter].alpha[1]:node[counter].alpha[0];// modify

        counter++;
    }
}
*/
/*
__kernel void gpuRunHaarClassifierCascade_ScaleWindow(
                          constant GpuHidHaarClassifierCascade * _cascade,
                          global GpuHidHaarStageClassifier * stagecascadeptr,
                          //global GpuHidHaarClassifier * classifierptr,
                          global GpuHidHaarTreeNode * nodeptr,
                          global int * sum,
                          global float * sqsum,
                          global int * _candidate,
                          int pixel_step,
                          int cols,
                          int rows,
                          int start_stage,
                          int end_stage,
                          //int counts,
                          int nodenum,
                          int ystep,
                          int detect_width,
                          //int detect_height,
                          int loopcount,
                          int outputstep)
                          //float scalefactor)
{
unsigned int x1 = get_global_id(0);
unsigned int y1 = get_global_id(1);
int p_offset;
int m, n;
int result;
int counter;
float mean, variance_norm_factor;
for(int i=0;i<loopcount;i++)
{
constant GpuHidHaarClassifierCascade * cascade = _cascade + i;
global int * candidate = _candidate + i*outputstep;
int window_width = cascade->p1 - cascade->p0;
int window_height = window_width;
result = 1;
counter = 0;
unsigned int x = mul24(x1,ystep);
unsigned int y = mul24(y1,ystep);
if((x < cols - window_width - 1) && (y < rows - window_height -1))
{
global GpuHidHaarStageClassifier *stagecascade = stagecascadeptr +cascade->count*i+ start_stage;
//global GpuHidHaarClassifier      *classifier   = classifierptr;
global GpuHidHaarTreeNode        *node         = nodeptr + nodenum*i;

p_offset = mad24(y, pixel_step, x);// modify

mean = (*(sum + p_offset + (int)cascade->p0) - *(sum + p_offset + (int)cascade->p1) -
    *(sum + p_offset + (int)cascade->p2) + *(sum + p_offset + (int)cascade->p3))
    *cascade->inv_window_area;

variance_norm_factor = *(sqsum + p_offset + cascade->p0) - *(sqsum + cascade->p1 + p_offset) -
                    *(sqsum + p_offset + cascade->p2) + *(sqsum + cascade->p3 + p_offset);
variance_norm_factor = variance_norm_factor * cascade->inv_window_area - mean * mean;
variance_norm_factor = variance_norm_factor >=0.f ? sqrt(variance_norm_factor) : 1;//modify

// if( cascade->is_stump_based )
//{
for( m = start_stage; m < end_stage; m++ )
{
float stage_sum = 0.f;
float t,  classsum;
GpuHidHaarTreeNode t1;

//#pragma unroll
for( n = 0; n < stagecascade->count; n++ )
{
     t1 = *(node + counter);
     t  = t1.threshold * variance_norm_factor;
     classsum = calc_sum1(t1, p_offset ,0) * t1.weight[0] + calc_sum1(t1, p_offset ,1) * t1.weight[1];

     if((t1.p0[2]) && (!stagecascade->two_rects))
         classsum += calc_sum1(t1, p_offset, 2) * t1.weight[2];

     stage_sum += classsum >= t ? t1.alpha[1] : t1.alpha[0];// modify
     counter++;
}

if (stage_sum < stagecascade->threshold)
{
    result = 0;
    break;
}

stagecascade++;

}
if(result)
{
    candidate[4 * (y1 * detect_width + x1)]     = x;
    candidate[4 * (y1 * detect_width + x1) + 1] = y;
    candidate[4 * (y1 * detect_width + x1)+2]     = window_width;
    candidate[4 * (y1 * detect_width + x1) + 3] = window_height;
}
//}
}
}
}
*/

//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
//    Niko Li, newlife20080214@gmail.com
//    Wang Weiyan, wangweiyanster@gmail.com
//    Jia Haipeng, jiahaipeng95@gmail.com
//    Nathan, liujun@multicorewareinc.com
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other oclMaterials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors as is and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//

#pragma OPENCL EXTENSION cl_amd_printf : enable
#define CV_HAAR_FEATURE_MAX           3

#define calc_sum(rect,offset)        (sum[(rect).p0+offset] - sum[(rect).p1+offset] - sum[(rect).p2+offset] + sum[(rect).p3+offset])
#define calc_sum1(rect,offset,i)     (sum[(rect).p0[i]+offset] - sum[(rect).p1[i]+offset] - sum[(rect).p2[i]+offset] + sum[(rect).p3[i]+offset])

typedef int   sumtype;
typedef float sqsumtype;

typedef struct  __attribute__((aligned (128)))  GpuHidHaarFeature
{
    struct __attribute__((aligned (32)))
{
    int p0 __attribute__((aligned (4)));
    int p1 __attribute__((aligned (4)));
    int p2 __attribute__((aligned (4)));
    int p3 __attribute__((aligned (4)));
    float weight __attribute__((aligned (4)));
}
rect[CV_HAAR_FEATURE_MAX] __attribute__((aligned (32)));
}
GpuHidHaarFeature;

typedef struct __attribute__((aligned (128) )) GpuHidHaarTreeNode
{
    int p[CV_HAAR_FEATURE_MAX][4] __attribute__((aligned (64)));
    float weight[CV_HAAR_FEATURE_MAX] /*__attribute__((aligned (16)))*/;
    float threshold /*__attribute__((aligned (4)))*/;
    float alpha[2] __attribute__((aligned (8)));
    int left __attribute__((aligned (4)));
    int right __attribute__((aligned (4)));
}
GpuHidHaarTreeNode;

typedef struct __attribute__((aligned (32))) GpuHidHaarClassifier
{
    int count __attribute__((aligned (4)));
    GpuHidHaarTreeNode* node __attribute__((aligned (8)));
    float* alpha __attribute__((aligned (8)));
}
GpuHidHaarClassifier;

typedef struct __attribute__((aligned (64))) GpuHidHaarStageClassifier
{
    int  count __attribute__((aligned (4)));
    float threshold __attribute__((aligned (4)));
    int two_rects __attribute__((aligned (4)));
    int reserved0 __attribute__((aligned (8)));
    int reserved1 __attribute__((aligned (8)));
    int reserved2 __attribute__((aligned (8)));
    int reserved3 __attribute__((aligned (8)));
}
GpuHidHaarStageClassifier;

typedef struct __attribute__((aligned (64))) GpuHidHaarClassifierCascade
{
    int  count __attribute__((aligned (4)));
    int  is_stump_based __attribute__((aligned (4)));
    int  has_tilted_features __attribute__((aligned (4)));
    int  is_tree __attribute__((aligned (4)));
    int pq0 __attribute__((aligned (4)));
    int pq1 __attribute__((aligned (4)));
    int pq2 __attribute__((aligned (4)));
    int pq3 __attribute__((aligned (4)));
    int p0 __attribute__((aligned (4)));
    int p1 __attribute__((aligned (4)));
    int p2 __attribute__((aligned (4)));
    int p3 __attribute__((aligned (4)));
    float inv_window_area __attribute__((aligned (4)));
} GpuHidHaarClassifierCascade;

__kernel void __attribute__((reqd_work_group_size(8,8,1)))gpuRunHaarClassifierCascade(
    global GpuHidHaarStageClassifier * stagecascadeptr,
    global int4 * info,
    global GpuHidHaarTreeNode * nodeptr,
    global const int * restrict sum1,
    global const float * restrict sqsum1,
    global int4 * candidate,
    const int pixelstep,
    const int loopcount,
    const int start_stage,
    const int split_stage,
    const int end_stage,
    const int startnode,
    const int splitnode,
    const int4 p,
    const int4 pq,
    const float correction)
{
    int grpszx = get_local_size(0);
    int grpszy = get_local_size(1);
    int grpnumx = get_num_groups(0);
    int grpidx = get_group_id(0);
    int lclidx = get_local_id(0);
    int lclidy = get_local_id(1);

    int lcl_sz = mul24(grpszx,grpszy);
    int lcl_id = mad24(lclidy,grpszx,lclidx);

    __local int lclshare[1024];
    __local int* lcldata = lclshare;//for save win data
    __local int* glboutindex = lcldata + 28*28;//for save global out index
    __local int* lclcount = glboutindex + 1;//for save the numuber of temp pass pixel
    __local int* lcloutindex = lclcount + 1;//for save info of temp pass pixel
    __local float* partialsum = (__local float*)(lcloutindex + (lcl_sz<<1));
    glboutindex[0]=0;
    int outputoff = mul24(grpidx,256);

    //assume window size is 20X20
#define WINDOWSIZE 20+1
    //make sure readwidth is the multiple of 4
    //ystep =1, from host code
    int readwidth = ((grpszx-1 + WINDOWSIZE+3)>>2)<<2;
    int readheight = grpszy-1+WINDOWSIZE;
    int read_horiz_cnt = readwidth >> 2;//each read int4
    int total_read = mul24(read_horiz_cnt,readheight);
    int read_loop = (total_read + lcl_sz - 1) >> 6;
    candidate[outputoff+(lcl_id<<2)] = (int4)0;
    candidate[outputoff+(lcl_id<<2)+1] = (int4)0;
    candidate[outputoff+(lcl_id<<2)+2] = (int4)0;
    candidate[outputoff+(lcl_id<<2)+3] = (int4)0;
    for(int scalei = 0; scalei <loopcount; scalei++)
    {
        int4 scaleinfo1= info[scalei];
        int width = (scaleinfo1.x & 0xffff0000) >> 16;
        int height = scaleinfo1.x & 0xffff;
        int grpnumperline =(scaleinfo1.y & 0xffff0000) >> 16;
        int totalgrp = scaleinfo1.y & 0xffff;
        int imgoff = scaleinfo1.z;
        float factor = as_float(scaleinfo1.w);

        __global const int * sum = sum1 + imgoff;
        __global const float * sqsum = sqsum1 + imgoff;
        for(int grploop=grpidx; grploop<totalgrp; grploop+=grpnumx)
        {
            int grpidy = grploop / grpnumperline;
            int grpidx = grploop - mul24(grpidy, grpnumperline);
            int x = mad24(grpidx,grpszx,lclidx);
            int y = mad24(grpidy,grpszy,lclidy);
            int grpoffx = x-lclidx;
            int grpoffy = y-lclidy;

            for(int i=0; i<read_loop; i++)
            {
                int pos_id = mad24(i,lcl_sz,lcl_id);
                pos_id = pos_id < total_read ? pos_id : 0;

                int lcl_y = pos_id / read_horiz_cnt;
                int lcl_x = pos_id - mul24(lcl_y, read_horiz_cnt);

                int glb_x = grpoffx + (lcl_x<<2);
                int glb_y = grpoffy + lcl_y;

                int glb_off = mad24(glb_y,pixelstep,glb_x);
                int4 data = *(__global int4*)&sum[glb_off];
                int lcl_off = mad24(lcl_y, readwidth, lcl_x<<2);

                vstore4(data, 0, &lcldata[lcl_off]);
            }

            lcloutindex[lcl_id] = 0;
            lclcount[0] = 0;
            int result = 1;
            int nodecounter= startnode;
            float mean, variance_norm_factor;
            barrier(CLK_LOCAL_MEM_FENCE);

            int lcl_off = mad24(lclidy,readwidth,lclidx);
            int4 cascadeinfo1, cascadeinfo2;
            cascadeinfo1 = p;
            cascadeinfo2 = pq;

            cascadeinfo1.x +=lcl_off;
            cascadeinfo1.z +=lcl_off;
            mean = (lcldata[mad24(cascadeinfo1.y,readwidth,cascadeinfo1.x)] - lcldata[mad24(cascadeinfo1.y,readwidth,cascadeinfo1.z)] -
                    lcldata[mad24(cascadeinfo1.w,readwidth,cascadeinfo1.x)] + lcldata[mad24(cascadeinfo1.w,readwidth,cascadeinfo1.z)])
                    *correction;

            int p_offset = mad24(y, pixelstep, x);

            cascadeinfo2.x +=p_offset;
            cascadeinfo2.z +=p_offset;
            variance_norm_factor =sqsum[mad24(cascadeinfo2.y, pixelstep, cascadeinfo2.x)] - sqsum[mad24(cascadeinfo2.y, pixelstep, cascadeinfo2.z)] -
                                    sqsum[mad24(cascadeinfo2.w, pixelstep, cascadeinfo2.x)] + sqsum[mad24(cascadeinfo2.w, pixelstep, cascadeinfo2.z)];

            variance_norm_factor = variance_norm_factor * correction - mean * mean;
            variance_norm_factor = variance_norm_factor >=0.f ? sqrt(variance_norm_factor) : 1.f;

            for(int stageloop = start_stage; (stageloop < split_stage)  && result; stageloop++ )
            {
                float stage_sum = 0.f;
                int2 stageinfo = *(global int2*)(stagecascadeptr+stageloop);
                float stagethreshold = as_float(stageinfo.y);
                for(int nodeloop = 0; nodeloop < stageinfo.x; nodeloop++ )
                {
                    __global GpuHidHaarTreeNode* currentnodeptr = (nodeptr + nodecounter);

                    int4 info1 = *(__global int4*)(&(currentnodeptr->p[0][0]));
                    int4 info2 = *(__global int4*)(&(currentnodeptr->p[1][0]));
                    int4 info3 = *(__global int4*)(&(currentnodeptr->p[2][0]));
                    float4 w = *(__global float4*)(&(currentnodeptr->weight[0]));
                    float2 alpha2 = *(__global float2*)(&(currentnodeptr->alpha[0]));
                    float nodethreshold  = w.w * variance_norm_factor;

                    info1.x +=lcl_off;
                    info1.z +=lcl_off;
                    info2.x +=lcl_off;
                    info2.z +=lcl_off;

                    float classsum = (lcldata[mad24(info1.y,readwidth,info1.x)] - lcldata[mad24(info1.y,readwidth,info1.z)] -
                                        lcldata[mad24(info1.w,readwidth,info1.x)] + lcldata[mad24(info1.w,readwidth,info1.z)]) * w.x;

                    classsum += (lcldata[mad24(info2.y,readwidth,info2.x)] - lcldata[mad24(info2.y,readwidth,info2.z)] -
                                    lcldata[mad24(info2.w,readwidth,info2.x)] + lcldata[mad24(info2.w,readwidth,info2.z)]) * w.y;

                    info3.x +=lcl_off;
                    info3.z +=lcl_off;
                    classsum += (lcldata[mad24(info3.y,readwidth,info3.x)] - lcldata[mad24(info3.y,readwidth,info3.z)] -
                                    lcldata[mad24(info3.w,readwidth,info3.x)] + lcldata[mad24(info3.w,readwidth,info3.z)]) * w.z;

                    stage_sum += classsum >= nodethreshold ? alpha2.y : alpha2.x;
                    nodecounter++;
                }

                result = (stage_sum >= stagethreshold);
            }

            if(result && (x < width) && (y < height))
            {
                int queueindex = atomic_inc(lclcount);
                lcloutindex[queueindex<<1] = (lclidy << 16) | lclidx;
                lcloutindex[(queueindex<<1)+1] = as_int(variance_norm_factor);
            }
            barrier(CLK_LOCAL_MEM_FENCE);
            int queuecount  = lclcount[0];
            barrier(CLK_LOCAL_MEM_FENCE);
            nodecounter = splitnode;
            for(int stageloop = split_stage; stageloop< end_stage && queuecount>0; stageloop++)
            {
                lclcount[0]=0;
                barrier(CLK_LOCAL_MEM_FENCE);

                int2 stageinfo = *(global int2*)(stagecascadeptr+stageloop);
                float stagethreshold = as_float(stageinfo.y);

                int perfscale = queuecount > 4 ? 3 : 2;
                int queuecount_loop = (queuecount + (1<<perfscale)-1) >> perfscale;
                int lcl_compute_win = lcl_sz >> perfscale;
                int lcl_compute_win_id = (lcl_id >>(6-perfscale));
                int lcl_loops = (stageinfo.x + lcl_compute_win -1) >> (6-perfscale);
                int lcl_compute_id = lcl_id - (lcl_compute_win_id << (6-perfscale));
                for(int queueloop=0; queueloop<queuecount_loop; queueloop++)
                {
                    float stage_sum = 0.f;
                    int temp_coord = lcloutindex[lcl_compute_win_id<<1];
                    float variance_norm_factor = as_float(lcloutindex[(lcl_compute_win_id<<1)+1]);
                    int queue_pixel = mad24(((temp_coord  & (int)0xffff0000)>>16),readwidth,temp_coord & 0xffff);

                    if(lcl_compute_win_id < queuecount)
                    {

                        int tempnodecounter = lcl_compute_id;
                        float part_sum = 0.f;
                        for(int lcl_loop=0; lcl_loop<lcl_loops && tempnodecounter<stageinfo.x; lcl_loop++)
                        {
                            __global GpuHidHaarTreeNode* currentnodeptr = (nodeptr + nodecounter + tempnodecounter);

                            int4 info1 = *(__global int4*)(&(currentnodeptr->p[0][0]));
                            int4 info2 = *(__global int4*)(&(currentnodeptr->p[1][0]));
                            int4 info3 = *(__global int4*)(&(currentnodeptr->p[2][0]));
                            float4 w = *(__global float4*)(&(currentnodeptr->weight[0]));
                            float2 alpha2 = *(__global float2*)(&(currentnodeptr->alpha[0]));
                            float nodethreshold  = w.w * variance_norm_factor;

                            info1.x +=queue_pixel;
                            info1.z +=queue_pixel;
                            info2.x +=queue_pixel;
                            info2.z +=queue_pixel;

                            float classsum = (lcldata[mad24(info1.y,readwidth,info1.x)] - lcldata[mad24(info1.y,readwidth,info1.z)] -
                                                lcldata[mad24(info1.w,readwidth,info1.x)] + lcldata[mad24(info1.w,readwidth,info1.z)]) * w.x;

                            classsum += (lcldata[mad24(info2.y,readwidth,info2.x)] - lcldata[mad24(info2.y,readwidth,info2.z)] -
                                            lcldata[mad24(info2.w,readwidth,info2.x)] + lcldata[mad24(info2.w,readwidth,info2.z)]) * w.y;

                            info3.x +=queue_pixel;
                            info3.z +=queue_pixel;
                            classsum += (lcldata[mad24(info3.y,readwidth,info3.x)] - lcldata[mad24(info3.y,readwidth,info3.z)] -
                                            lcldata[mad24(info3.w,readwidth,info3.x)] + lcldata[mad24(info3.w,readwidth,info3.z)]) * w.z;

                            part_sum += classsum >= nodethreshold ? alpha2.y : alpha2.x;
                            tempnodecounter +=lcl_compute_win;
                        }//end for(int lcl_loop=0;lcl_loop<lcl_loops;lcl_loop++)
                        partialsum[lcl_id]=part_sum;
                    }
                    barrier(CLK_LOCAL_MEM_FENCE);
                    if(lcl_compute_win_id < queuecount)
                    {
                        for(int i=0; i<lcl_compute_win && (lcl_compute_id==0); i++)
                        {
                            stage_sum += partialsum[lcl_id+i];
                        }
                        if(stage_sum >= stagethreshold && (lcl_compute_id==0))
                        {
                            int queueindex = atomic_inc(lclcount);
                            lcloutindex[queueindex<<1] = temp_coord;
                            lcloutindex[(queueindex<<1)+1] = as_int(variance_norm_factor);
                        }
                        lcl_compute_win_id +=(1<<perfscale);
                    }
                    barrier(CLK_LOCAL_MEM_FENCE);
                }//end for(int queueloop=0;queueloop<queuecount_loop;queueloop++)

                queuecount = lclcount[0];
                barrier(CLK_LOCAL_MEM_FENCE);
                nodecounter += stageinfo.x;
            }//end for(int stageloop = splitstage; stageloop< endstage && queuecount>0;stageloop++)

            if(lcl_id<queuecount)
            {
                int temp = lcloutindex[lcl_id<<1];
                int x = mad24(grpidx,grpszx,temp & 0xffff);
                int y = mad24(grpidy,grpszy,((temp & (int)0xffff0000) >> 16));
                temp = glboutindex[0];
                int4 candidate_result;
                candidate_result.zw = (int2)convert_int_rtn(factor*20.f);
                candidate_result.x = convert_int_rtn(x*factor);
                candidate_result.y = convert_int_rtn(y*factor);
                atomic_inc(glboutindex);
                candidate[outputoff+temp+lcl_id] = candidate_result;
            }
            barrier(CLK_LOCAL_MEM_FENCE);
        }//end for(int grploop=grpidx;grploop<totalgrp;grploop+=grpnumx)
    }//end for(int scalei = 0; scalei <loopcount; scalei++)
}

/*
if(stagecascade->two_rects)
{
    #pragma unroll
    for( n = 0; n < stagecascade->count; n++ )
    {
        t1 = *(node + counter);
        t = t1.threshold * variance_norm_factor;
        classsum = calc_sum1(t1,p_offset,0) * t1.weight[0];

        classsum  += calc_sum1(t1, p_offset,1) * t1.weight[1];
        stage_sum += classsum >= t ? t1.alpha[1]:t1.alpha[0];

        counter++;
    }
}
else
{
    #pragma unroll
    for( n = 0; n < stagecascade->count; n++ )
    {
        t = node[counter].threshold*variance_norm_factor;
        classsum = calc_sum1(node[counter],p_offset,0) * node[counter].weight[0];
        classsum += calc_sum1(node[counter],p_offset,1) * node[counter].weight[1];

        if( node[counter].p0[2] )
            classsum += calc_sum1(node[counter],p_offset,2) * node[counter].weight[2];

        stage_sum += classsum >= t ? node[counter].alpha[1]:node[counter].alpha[0];// modify

        counter++;
    }
}
*/
/*
__kernel void gpuRunHaarClassifierCascade_ScaleWindow(
                          constant GpuHidHaarClassifierCascade * _cascade,
                          global GpuHidHaarStageClassifier * stagecascadeptr,
                          //global GpuHidHaarClassifier * classifierptr,
                          global GpuHidHaarTreeNode * nodeptr,
                          global int * sum,
                          global float * sqsum,
                          global int * _candidate,
                          int pixel_step,
                          int cols,
                          int rows,
                          int start_stage,
                          int end_stage,
                          //int counts,
                          int nodenum,
                          int ystep,
                          int detect_width,
                          //int detect_height,
                          int loopcount,
                          int outputstep)
                          //float scalefactor)
{
unsigned int x1 = get_global_id(0);
unsigned int y1 = get_global_id(1);
int p_offset;
int m, n;
int result;
int counter;
float mean, variance_norm_factor;
for(int i=0;i<loopcount;i++)
{
constant GpuHidHaarClassifierCascade * cascade = _cascade + i;
global int * candidate = _candidate + i*outputstep;
int window_width = cascade->p1 - cascade->p0;
int window_height = window_width;
result = 1;
counter = 0;
unsigned int x = mul24(x1,ystep);
unsigned int y = mul24(y1,ystep);
if((x < cols - window_width - 1) && (y < rows - window_height -1))
{
global GpuHidHaarStageClassifier *stagecascade = stagecascadeptr +cascade->count*i+ start_stage;
//global GpuHidHaarClassifier      *classifier   = classifierptr;
global GpuHidHaarTreeNode        *node         = nodeptr + nodenum*i;

p_offset = mad24(y, pixel_step, x);// modify

mean = (*(sum + p_offset + (int)cascade->p0) - *(sum + p_offset + (int)cascade->p1) -
    *(sum + p_offset + (int)cascade->p2) + *(sum + p_offset + (int)cascade->p3))
    *cascade->inv_window_area;

variance_norm_factor = *(sqsum + p_offset + cascade->p0) - *(sqsum + cascade->p1 + p_offset) -
                    *(sqsum + p_offset + cascade->p2) + *(sqsum + cascade->p3 + p_offset);
variance_norm_factor = variance_norm_factor * cascade->inv_window_area - mean * mean;
variance_norm_factor = variance_norm_factor >=0.f ? sqrt(variance_norm_factor) : 1;//modify

// if( cascade->is_stump_based )
//{
for( m = start_stage; m < end_stage; m++ )
{
float stage_sum = 0.f;
float t,  classsum;
GpuHidHaarTreeNode t1;

//#pragma unroll
for( n = 0; n < stagecascade->count; n++ )
{
     t1 = *(node + counter);
     t  = t1.threshold * variance_norm_factor;
     classsum = calc_sum1(t1, p_offset ,0) * t1.weight[0] + calc_sum1(t1, p_offset ,1) * t1.weight[1];

     if((t1.p0[2]) && (!stagecascade->two_rects))
         classsum += calc_sum1(t1, p_offset, 2) * t1.weight[2];

     stage_sum += classsum >= t ? t1.alpha[1] : t1.alpha[0];// modify
     counter++;
}

if (stage_sum < stagecascade->threshold)
{
    result = 0;
    break;
}

stagecascade++;

}
if(result)
{
    candidate[4 * (y1 * detect_width + x1)]     = x;
    candidate[4 * (y1 * detect_width + x1) + 1] = y;
    candidate[4 * (y1 * detect_width + x1)+2]     = window_width;
    candidate[4 * (y1 * detect_width + x1) + 3] = window_height;
}
//}
}
}
}
*/


Fix #10557

Fix #10557

Fix overflow bugs in conversion from NV12 VA-surface/D3D11texture2D to OpenCL UMat

/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
//    Jia Haipeng, jiahaipeng95@gmail.com
//
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors as is and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the copyright holders or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#ifdef DOUBLE_SUPPORT
#ifdef cl_amd_fp64
#pragma OPENCL EXTENSION cl_amd_fp64:enable
#elif defined cl_khr_fp64
#pragma OPENCL EXTENSION cl_khr_fp64:enable
#endif
#endif

#ifdef INTEL_DEVICE
#pragma OPENCL FP_CONTRACT ON
#pragma OPENCL FP_FAST_FMAF ON
#pragma OPENCL FP_FAST_FMA ON
#endif

static
__constant
float c_YUV2RGBCoeffs_420[5] =
{
     1.163999557f,
     2.017999649f,
    -0.390999794f,
    -0.812999725f,
     1.5959997177f
};

static const __constant float CV_8U_MAX         = 255.0f;
static const __constant float CV_8U_HALF        = 128.0f;
static const __constant float BT601_BLACK_RANGE = 16.0f;
static const __constant float CV_8U_SCALE       = 1.0f / 255.0f;
static const __constant float d1                = BT601_BLACK_RANGE / CV_8U_MAX;
static const __constant float d2                = CV_8U_HALF / CV_8U_MAX;

#define NCHANNELS 3

__kernel
void YUV2BGR_NV12_8u(
    read_only image2d_t imgY,
    read_only image2d_t imgUV,
    __global unsigned char* pBGR,
   int bgrStep,
   int cols,
   int rows)
{
    int x = get_global_id(0);
    int y = get_global_id(1);

    if (x + 1 < cols)
    {
        if (y + 1 < rows)
        {
            __global uchar* pDstRow1 = pBGR + mad24(y, bgrStep, mad24(x, NCHANNELS, 0));
            __global uchar* pDstRow2 = pDstRow1 + bgrStep;

            float4 Y1 = read_imagef(imgY, (int2)(x+0, y+0));
            float4 Y2 = read_imagef(imgY, (int2)(x+1, y+0));
            float4 Y3 = read_imagef(imgY, (int2)(x+0, y+1));
            float4 Y4 = read_imagef(imgY, (int2)(x+1, y+1));

            float4 UV = read_imagef(imgUV, (int2)(x/2, y/2)) - d2;

            __constant float* coeffs = c_YUV2RGBCoeffs_420;

            Y1 = max(0.f, Y1 - d1) * coeffs[0];
            Y2 = max(0.f, Y2 - d1) * coeffs[0];
            Y3 = max(0.f, Y3 - d1) * coeffs[0];
            Y4 = max(0.f, Y4 - d1) * coeffs[0];

            float ruv = fma(coeffs[4], UV.y, 0.0f);
            float guv = fma(coeffs[3], UV.y, fma(coeffs[2], UV.x, 0.0f));
            float buv = fma(coeffs[1], UV.x, 0.0f);

            float R1 = (Y1.x + ruv) * CV_8U_MAX;
            float G1 = (Y1.x + guv) * CV_8U_MAX;
            float B1 = (Y1.x + buv) * CV_8U_MAX;

            float R2 = (Y2.x + ruv) * CV_8U_MAX;
            float G2 = (Y2.x + guv) * CV_8U_MAX;
            float B2 = (Y2.x + buv) * CV_8U_MAX;

            float R3 = (Y3.x + ruv) * CV_8U_MAX;
            float G3 = (Y3.x + guv) * CV_8U_MAX;
            float B3 = (Y3.x + buv) * CV_8U_MAX;

            float R4 = (Y4.x + ruv) * CV_8U_MAX;
            float G4 = (Y4.x + guv) * CV_8U_MAX;
            float B4 = (Y4.x + buv) * CV_8U_MAX;

            pDstRow1[0*NCHANNELS + 0] = convert_uchar_sat(B1);
            pDstRow1[0*NCHANNELS + 1] = convert_uchar_sat(G1);
            pDstRow1[0*NCHANNELS + 2] = convert_uchar_sat(R1);

            pDstRow1[1*NCHANNELS + 0] = convert_uchar_sat(B2);
            pDstRow1[1*NCHANNELS + 1] = convert_uchar_sat(G2);
            pDstRow1[1*NCHANNELS + 2] = convert_uchar_sat(R2);

            pDstRow2[0*NCHANNELS + 0] = convert_uchar_sat(B3);
            pDstRow2[0*NCHANNELS + 1] = convert_uchar_sat(G3);
            pDstRow2[0*NCHANNELS + 2] = convert_uchar_sat(R3);

            pDstRow2[1*NCHANNELS + 0] = convert_uchar_sat(B4);
            pDstRow2[1*NCHANNELS + 1] = convert_uchar_sat(G4);
            pDstRow2[1*NCHANNELS + 2] = convert_uchar_sat(R4);
        }
    }
}

static
__constant float c_RGB2YUVCoeffs_420[8] =
{
     0.256999969f,  0.50399971f,   0.09799957f,   -0.1479988098f,
    -0.2909994125f, 0.438999176f, -0.3679990768f, -0.0709991455f
};

__kernel
void BGR2YUV_NV12_8u(
    __global unsigned char* pBGR,
    int bgrStep,
    int cols,
    int rows,
    write_only image2d_t imgY,
    write_only image2d_t imgUV)
{
    int x = get_global_id(0);
    int y = get_global_id(1);

    if (x < cols)
    {
        if (y < rows)
        {
            __global const uchar* pSrcRow1 = pBGR + mad24(y, bgrStep, mad24(x, NCHANNELS, 0));
            __global const uchar* pSrcRow2 = pSrcRow1 + bgrStep;

            float4 src_pix1 = convert_float4(vload4(0, pSrcRow1 + 0*NCHANNELS)) * CV_8U_SCALE;
            float4 src_pix2 = convert_float4(vload4(0, pSrcRow1 + 1*NCHANNELS)) * CV_8U_SCALE;
            float4 src_pix3 = convert_float4(vload4(0, pSrcRow2 + 0*NCHANNELS)) * CV_8U_SCALE;
            float4 src_pix4 = convert_float4(vload4(0, pSrcRow2 + 1*NCHANNELS)) * CV_8U_SCALE;

            __constant float* coeffs = c_RGB2YUVCoeffs_420;

            float Y1 = fma(coeffs[0], src_pix1.z, fma(coeffs[1], src_pix1.y, fma(coeffs[2], src_pix1.x, d1)));
            float Y2 = fma(coeffs[0], src_pix2.z, fma(coeffs[1], src_pix2.y, fma(coeffs[2], src_pix2.x, d1)));
            float Y3 = fma(coeffs[0], src_pix3.z, fma(coeffs[1], src_pix3.y, fma(coeffs[2], src_pix3.x, d1)));
            float Y4 = fma(coeffs[0], src_pix4.z, fma(coeffs[1], src_pix4.y, fma(coeffs[2], src_pix4.x, d1)));

            float4 UV;
            UV.x = fma(coeffs[3], src_pix1.z, fma(coeffs[4], src_pix1.y, fma(coeffs[5], src_pix1.x, d2)));
            UV.y = fma(coeffs[5], src_pix1.z, fma(coeffs[6], src_pix1.y, fma(coeffs[7], src_pix1.x, d2)));

            write_imagef(imgY, (int2)(x+0, y+0), Y1);
            write_imagef(imgY, (int2)(x+1, y+0), Y2);
            write_imagef(imgY, (int2)(x+0, y+1), Y3);
            write_imagef(imgY, (int2)(x+1, y+1), Y4);

            write_imagef(imgUV, (int2)((x/2), (y/2)), UV);
        }
    }
}

/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
//    Jia Haipeng, jiahaipeng95@gmail.com
//
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors as is and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the copyright holders or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#ifdef DOUBLE_SUPPORT
#ifdef cl_amd_fp64
#pragma OPENCL EXTENSION cl_amd_fp64:enable
#elif defined cl_khr_fp64
#pragma OPENCL EXTENSION cl_khr_fp64:enable
#endif
#endif

#ifdef INTEL_DEVICE
#pragma OPENCL FP_CONTRACT ON
#pragma OPENCL FP_FAST_FMAF ON
#pragma OPENCL FP_FAST_FMA ON
#endif

static
__constant
float c_YUV2RGBCoeffs_420[5] =
{
     1.163999557f,
     2.017999649f,
    -0.390999794f,
    -0.812999725f,
     1.5959997177f
};

static const __constant float CV_8U_MAX         = 255.0f;
static const __constant float CV_8U_HALF        = 128.0f;
static const __constant float BT601_BLACK_RANGE = 16.0f;
static const __constant float CV_8U_SCALE       = 1.0f / 255.0f;
static const __constant float d1                = BT601_BLACK_RANGE / CV_8U_MAX;
static const __constant float d2                = CV_8U_HALF / CV_8U_MAX;

#define NCHANNELS 3

__kernel
void YUV2BGR_NV12_8u(
    read_only image2d_t imgY,
    read_only image2d_t imgUV,
    __global unsigned char* pBGR,
   int bgrStep,
   int cols,
   int rows)
{
    int x = get_global_id(0);
    int y = get_global_id(1);

    if (x < cols)
    {
        if (y < rows)
        {
            __global uchar* pDstRow1 = pBGR + mad24(y, bgrStep, mad24(x, NCHANNELS, 0));
            __global uchar* pDstRow2 = pDstRow1 + bgrStep;

            float4 Y1 = read_imagef(imgY, (int2)(x+0, y+0));
            float4 Y2 = read_imagef(imgY, (int2)(x+1, y+0));
            float4 Y3 = read_imagef(imgY, (int2)(x+0, y+1));
            float4 Y4 = read_imagef(imgY, (int2)(x+1, y+1));

            float4 UV = read_imagef(imgUV, (int2)(x/2, y/2)) - d2;

            __constant float* coeffs = c_YUV2RGBCoeffs_420;

            Y1 = max(0.f, Y1 - d1) * coeffs[0];
            Y2 = max(0.f, Y2 - d1) * coeffs[0];
            Y3 = max(0.f, Y3 - d1) * coeffs[0];
            Y4 = max(0.f, Y4 - d1) * coeffs[0];

            float ruv = fma(coeffs[4], UV.y, 0.0f);
            float guv = fma(coeffs[3], UV.y, fma(coeffs[2], UV.x, 0.0f));
            float buv = fma(coeffs[1], UV.x, 0.0f);

            float R1 = (Y1.x + ruv) * CV_8U_MAX;
            float G1 = (Y1.x + guv) * CV_8U_MAX;
            float B1 = (Y1.x + buv) * CV_8U_MAX;

            float R2 = (Y2.x + ruv) * CV_8U_MAX;
            float G2 = (Y2.x + guv) * CV_8U_MAX;
            float B2 = (Y2.x + buv) * CV_8U_MAX;

            float R3 = (Y3.x + ruv) * CV_8U_MAX;
            float G3 = (Y3.x + guv) * CV_8U_MAX;
            float B3 = (Y3.x + buv) * CV_8U_MAX;

            float R4 = (Y4.x + ruv) * CV_8U_MAX;
            float G4 = (Y4.x + guv) * CV_8U_MAX;
            float B4 = (Y4.x + buv) * CV_8U_MAX;

            pDstRow1[0*NCHANNELS + 0] = convert_uchar_sat(B1);
            pDstRow1[0*NCHANNELS + 1] = convert_uchar_sat(G1);
            pDstRow1[0*NCHANNELS + 2] = convert_uchar_sat(R1);

            pDstRow1[1*NCHANNELS + 0] = convert_uchar_sat(B2);
            pDstRow1[1*NCHANNELS + 1] = convert_uchar_sat(G2);
            pDstRow1[1*NCHANNELS + 2] = convert_uchar_sat(R2);

            pDstRow2[0*NCHANNELS + 0] = convert_uchar_sat(B3);
            pDstRow2[0*NCHANNELS + 1] = convert_uchar_sat(G3);
            pDstRow2[0*NCHANNELS + 2] = convert_uchar_sat(R3);

            pDstRow2[1*NCHANNELS + 0] = convert_uchar_sat(B4);
            pDstRow2[1*NCHANNELS + 1] = convert_uchar_sat(G4);
            pDstRow2[1*NCHANNELS + 2] = convert_uchar_sat(R4);
        }
    }
}

static
__constant float c_RGB2YUVCoeffs_420[8] =
{
     0.256999969f,  0.50399971f,   0.09799957f,   -0.1479988098f,
    -0.2909994125f, 0.438999176f, -0.3679990768f, -0.0709991455f
};

__kernel
void BGR2YUV_NV12_8u(
    __global unsigned char* pBGR,
    int bgrStep,
    int cols,
    int rows,
    write_only image2d_t imgY,
    write_only image2d_t imgUV)
{
    int x = get_global_id(0);
    int y = get_global_id(1);

    if (x < cols)
    {
        if (y < rows)
        {
            __global const uchar* pSrcRow1 = pBGR + mad24(y, bgrStep, mad24(x, NCHANNELS, 0));
            __global const uchar* pSrcRow2 = pSrcRow1 + bgrStep;

            float4 src_pix1 = convert_float4(vload4(0, pSrcRow1 + 0*NCHANNELS)) * CV_8U_SCALE;
            float4 src_pix2 = convert_float4(vload4(0, pSrcRow1 + 1*NCHANNELS)) * CV_8U_SCALE;
            float4 src_pix3 = convert_float4(vload4(0, pSrcRow2 + 0*NCHANNELS)) * CV_8U_SCALE;
            float4 src_pix4 = convert_float4(vload4(0, pSrcRow2 + 1*NCHANNELS)) * CV_8U_SCALE;

            __constant float* coeffs = c_RGB2YUVCoeffs_420;

            float Y1 = fma(coeffs[0], src_pix1.z, fma(coeffs[1], src_pix1.y, fma(coeffs[2], src_pix1.x, d1)));
            float Y2 = fma(coeffs[0], src_pix2.z, fma(coeffs[1], src_pix2.y, fma(coeffs[2], src_pix2.x, d1)));
            float Y3 = fma(coeffs[0], src_pix3.z, fma(coeffs[1], src_pix3.y, fma(coeffs[2], src_pix3.x, d1)));
            float Y4 = fma(coeffs[0], src_pix4.z, fma(coeffs[1], src_pix4.y, fma(coeffs[2], src_pix4.x, d1)));

            float4 UV;
            UV.x = fma(coeffs[3], src_pix1.z, fma(coeffs[4], src_pix1.y, fma(coeffs[5], src_pix1.x, d2)));
            UV.y = fma(coeffs[5], src_pix1.z, fma(coeffs[6], src_pix1.y, fma(coeffs[7], src_pix1.x, d2)));

            write_imagef(imgY, (int2)(x+0, y+0), Y1);
            write_imagef(imgY, (int2)(x+1, y+0), Y2);
            write_imagef(imgY, (int2)(x+0, y+1), Y3);
            write_imagef(imgY, (int2)(x+1, y+1), Y4);

            write_imagef(imgUV, (int2)((x/2), (y/2)), UV);
        }
    }
}


add const to remove build warnings on amd

// from SpatialMaxPooling.cu:

/*
 * Description:
 *    this function maxpools an input 4D tensor along dimensions 2 and 3
 *    4D input, 4D output, 4D argmax x and y
 */
kernel void maxpool(const global float *input_data, int input_offset,
    global float *output_data, int output_offset,
    const global float *indices_data, int indices_offset,
    int indices_x_offset,
    int indices_y_offset,
    int input_n, int input_h, int input_w,
    int kH, int kW, int dH, int dW)
{
  global const float *input = input_data + input_offset;
  global float *output = output_data + output_offset;
  global const float *indices_x = indices_data + indices_offset + indices_x_offset;
  global const float *indices_y = indices_data + indices_offset + indices_y_offset;

  // iterators
  int xx, yy;

  // output size
  const int output_w = (input_w - kW) / dW + 1;
  const int output_h = (input_h - kH) / dH + 1;

  // compute offsets based on thread/block ID
  int o = get_group_id(0);
  int i = o;
  //int k = get_group_id(0) % input_n;

  int xx_start = get_local_id(0);
  int xx_end = output_w;
  const int xx_step = get_local_size(0);

  int yy_start = get_local_size(1)*get_group_id(1) + get_local_id(1);
  int yy_end = output_h;
  const int yy_step = get_local_size(1)*get_num_groups(1);

  // select input/output plane
  output = output + o*output_w*output_h;
  input = input + i*input_w*input_h;
  indices_x = indices_x + o*output_w*output_h;
  indices_y = indices_y + o*output_w*output_h;

  // For all output pixels...
  for(yy = yy_start; yy < yy_end; yy+=yy_step) {
    for(xx = xx_start; xx < xx_end; xx+=xx_step) {
      // Compute the mean of the input image...
      global const float *ptr_input = input + yy*dH*input_w + xx*dW;
      global float *ptr_output = output + yy*output_w + xx;
      global const float *ptr_ind_x = indices_x + yy*output_w + xx;
      global const float *ptr_ind_y = indices_y + yy*output_w + xx;
      int argmax_x = -1;
      int argmax_y = -1;
      float max = -FLT_MAX;
      int kx, ky;
      for(ky = 0; ky < kH; ky++) {
        for(kx = 0; kx < kW; kx++) {
          float val = ptr_input[kx];
          if (val > max) {
            max = val;
            argmax_x = kx;
            argmax_y = ky;
          }
        }
        ptr_input += input_w; // next input line
      }
      // Update output and argmax
      *ptr_output = max;
      *ptr_ind_x = argmax_x + 1;
      *ptr_ind_y = argmax_y + 1;
    }
  }
}

/*
 * Description:
 *    this function computes the gradInput from weight and gradOutput
 */
kernel void maxgradinput(global float *gradInput_data, int gradInput_offset,
    global const float *gradOutput_data, int gradOutput_offset,
    global const float *indices_data, int indices_offset,
    int indices_x_offset, int indices_y_offset,
   int input_n, int input_h, int input_w,
   int kH, int kW, int dH, int dW)
{
  global float *gradInput = gradInput_data + gradInput_offset;
  global const float *gradOutput = gradOutput_data + gradOutput_offset;
  global const float *indices_x = indices_data + indices_offset + indices_x_offset;
  global const float *indices_y = indices_data + indices_offset + indices_y_offset;

  // iterators
  int xx, yy;

  // output size
  int output_w = (input_w - kW) / dW + 1;
  int output_h = (input_h - kH) / dH + 1;

  // compute offsets based on thread/block ID
  int o = get_group_id(0);
  int i = o;
  //int k = get_group_id(0) % input_n;

  int xx_start = get_local_id(0);
  int xx_end = output_w;
  int xx_step = get_local_size(0);

  int yy_start = get_local_size(1)*get_group_id(1) + get_local_id(1);
  int yy_end = output_h;
  int yy_step = get_local_size(1)*get_num_groups(1);

  // select input/output plane
  gradOutput = gradOutput + o*output_w*output_h;
  gradInput = gradInput + i*input_w*input_h;
  indices_x = indices_x + o*output_w*output_h;
  indices_y = indices_y + o*output_w*output_h;

  // compute gradInput
  for(yy = yy_start; yy < yy_end; yy+=yy_step) {
    for(xx = xx_start; xx < xx_end; xx+=xx_step) {
      global float *ptr_gradInput = gradInput + yy*dH*input_w + xx*dW;
      global const float *ptr_gradOutput = gradOutput + yy*output_w + xx;
      global const float *ptr_ind_x = indices_x + yy*output_w + xx;
      global const float *ptr_ind_y = indices_y + yy*output_w + xx;
      float z = *ptr_gradOutput;

      int argmax_x = (*ptr_ind_x)-1;
      int argmax_y = (*ptr_ind_y)-1;

      ptr_gradInput[argmax_x + argmax_y*input_w] += z;
    }
  }
}

/*
 * Description:
 *    this function computes the gradInput from weight and gradOutput
 *    when kH != dH or kW != dW (uses atomic add)
 */
//kernel void atomicmaxgradinput(
//  global float *gradInput_data, int gradInput_offset,
//  global float *gradOutput_data, int gradOutput_offset,
//  global float *indices_data, int indices_offset,
//  int indices_x_offset, int indices_y_offset,
//  int input_n, int input_h, int input_w, int kH, int kW, int dH, int dW
//)
//{
//  global float *gradInput = gradInput_data + gradInput_offset;
//  global float *gradOutput = gradOutput_data + gradOutput_offset;
//  global float *indices_x = indices_data + indices_offset + indices_x_offset;
//  global float *indices_y = indices_data + indices_offset + indices_y_offset;

//  // iterators
//  int xx, yy;

//  // output size
//  int output_w = (input_w - kW) / dW + 1;
//  int output_h = (input_h - kH) / dH + 1;

//  // compute offsets based on thread/block ID
//  int o = get_group_id(0);
//  int i = o;
//  //int k = get_group_id(0) % input_n;

//  int xx_start = get_local_id(0);
//  int xx_end = output_w;
//  int xx_step = get_local_size(0);

//  int yy_start = get_local_size(1)*get_group_id(1) + get_local_id(1);
//  int yy_end = output_h;
//  int yy_step = get_local_size(1)*get_num_groups(1);

//  // select input/output plane
//  gradOutput = gradOutput + o*output_w*output_h;
//  gradInput = gradInput + i*input_w*input_h;
//  indices_x = indices_x + o*output_w*output_h;
//  indices_y = indices_y + o*output_w*output_h;

//  // compute gradInput
//  for(yy = yy_start; yy < yy_end; yy+=yy_step) {
//    for(xx = xx_start; xx < xx_end; xx+=xx_step) {
//      global float *ptr_gradInput = gradInput + yy*dH*input_w + xx*dW;
//      global float *ptr_gradOutput = gradOutput + yy*output_w + xx;
//      global float *ptr_ind_x = indices_x + yy*output_w + xx;
//      global float *ptr_ind_y = indices_y + yy*output_w + xx;
//      float z = *ptr_gradOutput;

//      int argmax_x = (*ptr_ind_x)-1;
//      int argmax_y = (*ptr_ind_y)-1;

//      // atomic add since different threads could update same variable
////      atomicAdd(&(ptr_gradInput[argmax_x + argmax_y*input_w]), z);
//      // hmmm, this doesnt work with float :-(  need another way...
//      atomic_add(&(ptr_gradInput[argmax_x + argmax_y*input_w]), z);
//    }
//  }
//}

// from SpatialMaxPooling.cu:

/*
 * Description:
 *    this function maxpools an input 4D tensor along dimensions 2 and 3
 *    4D input, 4D output, 4D argmax x and y
 */
kernel void maxpool(const global float *input_data, int input_offset,
    global float *output_data, int output_offset,
    const global float *indices_data, int indices_offset,
    int indices_x_offset,
    int indices_y_offset,
    int input_n, int input_h, int input_w,
    int kH, int kW, int dH, int dW)
{
  global float *input = input_data + input_offset;
  global float *output = output_data + output_offset;
  global float *indices_x = indices_data + indices_offset + indices_x_offset;
  global float *indices_y = indices_data + indices_offset + indices_y_offset;

  // iterators
  int xx, yy;

  // output size
  const int output_w = (input_w - kW) / dW + 1;
  const int output_h = (input_h - kH) / dH + 1;

  // compute offsets based on thread/block ID
  int o = get_group_id(0);
  int i = o;
  //int k = get_group_id(0) % input_n;

  int xx_start = get_local_id(0);
  int xx_end = output_w;
  const int xx_step = get_local_size(0);

  int yy_start = get_local_size(1)*get_group_id(1) + get_local_id(1);
  int yy_end = output_h;
  const int yy_step = get_local_size(1)*get_num_groups(1);

  // select input/output plane
  output = output + o*output_w*output_h;
  input = input + i*input_w*input_h;
  indices_x = indices_x + o*output_w*output_h;
  indices_y = indices_y + o*output_w*output_h;

  // For all output pixels...
  for(yy = yy_start; yy < yy_end; yy+=yy_step) {
    for(xx = xx_start; xx < xx_end; xx+=xx_step) {
      // Compute the mean of the input image...
      global float *ptr_input = input + yy*dH*input_w + xx*dW;
      global float *ptr_output = output + yy*output_w + xx;
      global float *ptr_ind_x = indices_x + yy*output_w + xx;
      global float *ptr_ind_y = indices_y + yy*output_w + xx;
      int argmax_x = -1;
      int argmax_y = -1;
      float max = -FLT_MAX;
      int kx, ky;
      for(ky = 0; ky < kH; ky++) {
        for(kx = 0; kx < kW; kx++) {
          float val = ptr_input[kx];
          if (val > max) {
            max = val;
            argmax_x = kx;
            argmax_y = ky;
          }
        }
        ptr_input += input_w; // next input line
      }
      // Update output and argmax
      *ptr_output = max;
      *ptr_ind_x = argmax_x + 1;
      *ptr_ind_y = argmax_y + 1;
    }
  }
}

/*
 * Description:
 *    this function computes the gradInput from weight and gradOutput
 */
kernel void maxgradinput(global float *gradInput_data, int gradInput_offset,
    global float *gradOutput_data, int gradOutput_offset,
    global float *indices_data, int indices_offset,
    int indices_x_offset, int indices_y_offset,
   int input_n, int input_h, int input_w,
   int kH, int kW, int dH, int dW)
{
  global float *gradInput = gradInput_data + gradInput_offset;
  global float *gradOutput = gradOutput_data + gradOutput_offset;
  global float *indices_x = indices_data + indices_offset + indices_x_offset;
  global float *indices_y = indices_data + indices_offset + indices_y_offset;

  // iterators
  int xx, yy;

  // output size
  int output_w = (input_w - kW) / dW + 1;
  int output_h = (input_h - kH) / dH + 1;

  // compute offsets based on thread/block ID
  int o = get_group_id(0);
  int i = o;
  //int k = get_group_id(0) % input_n;

  int xx_start = get_local_id(0);
  int xx_end = output_w;
  int xx_step = get_local_size(0);

  int yy_start = get_local_size(1)*get_group_id(1) + get_local_id(1);
  int yy_end = output_h;
  int yy_step = get_local_size(1)*get_num_groups(1);

  // select input/output plane
  gradOutput = gradOutput + o*output_w*output_h;
  gradInput = gradInput + i*input_w*input_h;
  indices_x = indices_x + o*output_w*output_h;
  indices_y = indices_y + o*output_w*output_h;

  // compute gradInput
  for(yy = yy_start; yy < yy_end; yy+=yy_step) {
    for(xx = xx_start; xx < xx_end; xx+=xx_step) {
      global float *ptr_gradInput = gradInput + yy*dH*input_w + xx*dW;
      global float *ptr_gradOutput = gradOutput + yy*output_w + xx;
      global float *ptr_ind_x = indices_x + yy*output_w + xx;
      global float *ptr_ind_y = indices_y + yy*output_w + xx;
      float z = *ptr_gradOutput;

      int argmax_x = (*ptr_ind_x)-1;
      int argmax_y = (*ptr_ind_y)-1;

      ptr_gradInput[argmax_x + argmax_y*input_w] += z;
    }
  }
}

/*
 * Description:
 *    this function computes the gradInput from weight and gradOutput
 *    when kH != dH or kW != dW (uses atomic add)
 */
//kernel void atomicmaxgradinput(
//  global float *gradInput_data, int gradInput_offset,
//  global float *gradOutput_data, int gradOutput_offset,
//  global float *indices_data, int indices_offset,
//  int indices_x_offset, int indices_y_offset,
//  int input_n, int input_h, int input_w, int kH, int kW, int dH, int dW
//)
//{
//  global float *gradInput = gradInput_data + gradInput_offset;
//  global float *gradOutput = gradOutput_data + gradOutput_offset;
//  global float *indices_x = indices_data + indices_offset + indices_x_offset;
//  global float *indices_y = indices_data + indices_offset + indices_y_offset;

//  // iterators
//  int xx, yy;

//  // output size
//  int output_w = (input_w - kW) / dW + 1;
//  int output_h = (input_h - kH) / dH + 1;

//  // compute offsets based on thread/block ID
//  int o = get_group_id(0);
//  int i = o;
//  //int k = get_group_id(0) % input_n;

//  int xx_start = get_local_id(0);
//  int xx_end = output_w;
//  int xx_step = get_local_size(0);

//  int yy_start = get_local_size(1)*get_group_id(1) + get_local_id(1);
//  int yy_end = output_h;
//  int yy_step = get_local_size(1)*get_num_groups(1);

//  // select input/output plane
//  gradOutput = gradOutput + o*output_w*output_h;
//  gradInput = gradInput + i*input_w*input_h;
//  indices_x = indices_x + o*output_w*output_h;
//  indices_y = indices_y + o*output_w*output_h;

//  // compute gradInput
//  for(yy = yy_start; yy < yy_end; yy+=yy_step) {
//    for(xx = xx_start; xx < xx_end; xx+=xx_step) {
//      global float *ptr_gradInput = gradInput + yy*dH*input_w + xx*dW;
//      global float *ptr_gradOutput = gradOutput + yy*output_w + xx;
//      global float *ptr_ind_x = indices_x + yy*output_w + xx;
//      global float *ptr_ind_y = indices_y + yy*output_w + xx;
//      float z = *ptr_gradOutput;

//      int argmax_x = (*ptr_ind_x)-1;
//      int argmax_y = (*ptr_ind_y)-1;

//      // atomic add since different threads could update same variable
////      atomicAdd(&(ptr_gradInput[argmax_x + argmax_y*input_w]), z);
//      // hmmm, this doesnt work with float :-(  need another way...
//      atomic_add(&(ptr_gradInput[argmax_x + argmax_y*input_w]), z);
//    }
//  }
//}


disable pragam unrool (#2011)

disable pragam unrool (#2011)


Epsilon minimum weight

Epsilon minimum weight

target_weight will easily hit 0.f if hY is less than 0.5f and z=true.
Epsilon is chosen as roughly 1/255 so that even dark pixels with a
weight of nearly 1.f will still have a strong influence on the filtered
pixel.

/* Deathray - An Avisynth plug-in filter for spatial/temporal non-local means de-noising.
 *
 * version 1.02
 *
 * Copyright 2013, Jawed Ashraf - Deathray@cupidity.f9.co.uk
 */

void Filter4(
	const		int2	target,					// tile coordinates of left-hand pixel of 4 pixels in a horizontal strip
	const		float	h,						// strength of denoising
	const		int		sample_expand,			// factor to expand sample radius
				float16	*target_window,			// a window of 10x7 pixels, centred upon the 4 pixels being filtered
	local		float	*sample_tile,			// factor to expand sample radius
	constant	float	*gaussian,				// 49 weights of guassian kernel
	const		int		reweight_target_pixel,	// when target plane is the sampling plane, the target pixel is reweighted
	const		int		target_min,				// target pixel is weighted using minimum weight of samples, not maximum
	const		int		balanced,				// balanced tonal range de-noising
				float4	*all_samples_average,	// running sum of weighted pixel values
				float4	*all_samples_weight,	// running sum of weights
				float4  *target_weight) {		// weight chosen from across all sample planes that will be used for target pixel

	// Computes the gaussian-weighted average of the target pixels' windows
	// against all sample windows from the tile.
	//
	// The sample windows nominally number 49, but sample_expand can be used
	// to increase the sampling radius. This introduces errors when 2 or more
	// but the errors are hard to see unless strong sigma and/or h values
	// are supplied. Error increases with sample_expand, producing visible
	// edges corresponding with the tile borders within the plane.

	int kernel_radius = 3;
	int sample_radius = kernel_radius * sample_expand;

	int2 sample_start = max(target - sample_radius, 3);
	int2 sample_end	  = (int2)(min(target.x + sample_radius, 41), min(target.y + sample_radius, 44));

	float4 sample_centre_pixel;
	const float4 invert = 1.f;					// bias difference with an inversion...
	const float4 factor = balanced ? 0.5f: 0.f;	// ... and range limiter, towards highlights and away from shadows
	int2 sample;
	for (sample.y = sample_start.y; sample.y <= sample_end.y; ++sample.y) {
		for (sample.x = sample_start.x; sample.x <= sample_end.x; ++sample.x) {
			int gaussian_position = 0;
			float4 euclidean_distance = 0.f;
			int target_row = 0;
			for (int y = -kernel_radius; y < kernel_radius + 1; ++y, ++target_row) {
				float16 sample_window_row = ReadTile16(sample.x - kernel_radius,
													   sample.y + y,
													   sample_tile);

				float4 diff = (invert - factor * target_window[target_row].s0123) * (target_window[target_row].s0123 - sample_window_row.s0123);
				euclidean_distance += gaussian[gaussian_position] * (diff * diff);
				diff = (invert - factor * target_window[target_row].s6789) * (target_window[target_row].s6789 - sample_window_row.s6789);
				euclidean_distance += gaussian[gaussian_position] * (diff * diff);

				diff = (invert - factor * target_window[target_row].s1234) * (target_window[target_row].s1234 - sample_window_row.s1234);
				euclidean_distance += gaussian[gaussian_position + 1] * (diff * diff);
				diff = (invert - factor * target_window[target_row].s5678) * (target_window[target_row].s5678 - sample_window_row.s5678);
				euclidean_distance += gaussian[gaussian_position + 1] * (diff * diff);

				diff = (invert - factor * target_window[target_row].s2345) * (target_window[target_row].s2345 - sample_window_row.s2345);
				euclidean_distance += gaussian[gaussian_position + 2] * (diff * diff);
				diff = (invert - factor * target_window[target_row].s4567) * (target_window[target_row].s4567 - sample_window_row.s4567);
				euclidean_distance += gaussian[gaussian_position + 2] * (diff * diff);

				diff = (invert - factor * target_window[target_row].s3456) * (target_window[target_row].s3456 - sample_window_row.s3456);
				euclidean_distance += gaussian[gaussian_position + 3] * (diff * diff);

				gaussian_position += 7;
			}

			float4 sample_weight = exp(-euclidean_distance / h);

			*target_weight = target_min
						   ? min(*target_weight, sample_weight)
						   : max(*target_weight, sample_weight);

			sample_weight = (sample.x == target.x && sample.y == target.y && reweight_target_pixel)
						  ? 0.f
						  : sample_weight;

			*all_samples_weight += sample_weight;

			sample_centre_pixel = ReadTile4(sample.x, sample.y, sample_tile);
			*all_samples_average += sample_weight * sample_centre_pixel;
		}
	}
	*target_weight = max(*target_weight, 0.004f);
	*all_samples_weight += reweight_target_pixel ? *target_weight : 0.f;

	sample_centre_pixel = ReadTile4(target.x, target.y, sample_tile);
	*all_samples_average +=  reweight_target_pixel ? *target_weight * sample_centre_pixel : 0.f;
}

/* Deathray - An Avisynth plug-in filter for spatial/temporal non-local means de-noising.
 *
 * version 1.02
 *
 * Copyright 2013, Jawed Ashraf - Deathray@cupidity.f9.co.uk
 */

void Filter4(
	const		int2	target,					// tile coordinates of left-hand pixel of 4 pixels in a horizontal strip
	const		float	h,						// strength of denoising
	const		int		sample_expand,			// factor to expand sample radius
				float16	*target_window,			// a window of 10x7 pixels, centred upon the 4 pixels being filtered
	local		float	*sample_tile,			// factor to expand sample radius
	constant	float	*gaussian,				// 49 weights of guassian kernel
	const		int		reweight_target_pixel,	// when target plane is the sampling plane, the target pixel is reweighted
	const		int		target_min,				// target pixel is weighted using minimum weight of samples, not maximum
	const		int		balanced,				// balanced tonal range de-noising
				float4	*all_samples_average,	// running sum of weighted pixel values
				float4	*all_samples_weight,	// running sum of weights
				float4  *target_weight) {		// weight chosen from across all sample planes that will be used for target pixel

	// Computes the gaussian-weighted average of the target pixels' windows
	// against all sample windows from the tile.
	//
	// The sample windows nominally number 49, but sample_expand can be used
	// to increase the sampling radius. This introduces errors when 2 or more
	// but the errors are hard to see unless strong sigma and/or h values
	// are supplied. Error increases with sample_expand, producing visible
	// edges corresponding with the tile borders within the plane.

	int kernel_radius = 3;
	int sample_radius = kernel_radius * sample_expand;

	int2 sample_start = max(target - sample_radius, 3);
	int2 sample_end	  = (int2)(min(target.x + sample_radius, 41), min(target.y + sample_radius, 44));

	float4 sample_centre_pixel;
	const float4 invert = 1.f;					// bias difference with an inversion...
	const float4 factor = balanced ? 0.5f: 0.f;	// ... and range limiter, towards highlights and away from shadows
	int2 sample;
	for (sample.y = sample_start.y; sample.y <= sample_end.y; ++sample.y) {
		for (sample.x = sample_start.x; sample.x <= sample_end.x; ++sample.x) {
			int gaussian_position = 0;
			float4 euclidean_distance = 0.f;
			int target_row = 0;
			for (int y = -kernel_radius; y < kernel_radius + 1; ++y, ++target_row) {
				float16 sample_window_row = ReadTile16(sample.x - kernel_radius,
													   sample.y + y,
													   sample_tile);

				float4 diff = (invert - factor * target_window[target_row].s0123) * (target_window[target_row].s0123 - sample_window_row.s0123);
				euclidean_distance += gaussian[gaussian_position] * (diff * diff);
				diff = (invert - factor * target_window[target_row].s6789) * (target_window[target_row].s6789 - sample_window_row.s6789);
				euclidean_distance += gaussian[gaussian_position] * (diff * diff);

				diff = (invert - factor * target_window[target_row].s1234) * (target_window[target_row].s1234 - sample_window_row.s1234);
				euclidean_distance += gaussian[gaussian_position + 1] * (diff * diff);
				diff = (invert - factor * target_window[target_row].s5678) * (target_window[target_row].s5678 - sample_window_row.s5678);
				euclidean_distance += gaussian[gaussian_position + 1] * (diff * diff);

				diff = (invert - factor * target_window[target_row].s2345) * (target_window[target_row].s2345 - sample_window_row.s2345);
				euclidean_distance += gaussian[gaussian_position + 2] * (diff * diff);
				diff = (invert - factor * target_window[target_row].s4567) * (target_window[target_row].s4567 - sample_window_row.s4567);
				euclidean_distance += gaussian[gaussian_position + 2] * (diff * diff);

				diff = (invert - factor * target_window[target_row].s3456) * (target_window[target_row].s3456 - sample_window_row.s3456);
				euclidean_distance += gaussian[gaussian_position + 3] * (diff * diff);

				gaussian_position += 7;
			}

			float4 sample_weight = exp(-euclidean_distance / h);

			*target_weight = target_min
						   ? min(*target_weight, sample_weight)
						   : max(*target_weight, sample_weight);

			sample_weight = (sample.x == target.x && sample.y == target.y && reweight_target_pixel)
						  ? 0.f
						  : sample_weight;

			*all_samples_weight += sample_weight;

			sample_centre_pixel = ReadTile4(sample.x, sample.y, sample_tile);
			*all_samples_average += sample_weight * sample_centre_pixel;
		}
	}

	*all_samples_weight += reweight_target_pixel ? *target_weight : 0.f;

	sample_centre_pixel = ReadTile4(target.x, target.y, sample_tile);
	*all_samples_average +=  reweight_target_pixel ? *target_weight * sample_centre_pixel : 0.f;
}