New optimization for canny

new hysteresis

delete whitespaces

fix problem with mad24

Dynamic work group size

dynamic work group size

Fix problem with warnings

Fix some problems with border

Another one fix

Delete trailing whitespaces

some changes

fix problem with warning
This commit is contained in:
U-KruchininD-ПК\KruchininD 2014-07-15 17:04:50 +04:00 committed by Dmitry
parent 0eb1c7edb1
commit 6ed168d3af
2 changed files with 446 additions and 558 deletions

View File

@ -97,7 +97,23 @@ static bool ippCanny(const Mat& _src, Mat& _dst, float low, float high)
static bool ocl_Canny(InputArray _src, OutputArray _dst, float low_thresh, float high_thresh, static bool ocl_Canny(InputArray _src, OutputArray _dst, float low_thresh, float high_thresh,
int aperture_size, bool L2gradient, int cn, const Size & size) int aperture_size, bool L2gradient, int cn, const Size & size)
{ {
UMat dx(size, CV_16SC(cn)), dy(size, CV_16SC(cn)); UMat map;
const ocl::Device &dev = ocl::Device::getDefault();
int max_wg_size = (int)dev.maxWorkGroupSize();
int lSizeX = 32;
int lSizeY = max_wg_size / 32;
if (lSizeY == 0)
{
lSizeX = 16;
lSizeY = max_wg_size / 16;
}
if (lSizeY == 0)
{
lSizeY = 1;
}
if (L2gradient) if (L2gradient)
{ {
@ -110,144 +126,103 @@ static bool ocl_Canny(InputArray _src, OutputArray _dst, float low_thresh, float
high_thresh *= high_thresh; high_thresh *= high_thresh;
} }
int low = cvFloor(low_thresh), high = cvFloor(high_thresh); int low = cvFloor(low_thresh), high = cvFloor(high_thresh);
Size esize(size.width + 2, size.height + 2);
UMat mag;
size_t globalsize[2] = { size.width, size.height }, localsize[2] = { 16, 16 };
if (aperture_size == 3 && !_src.isSubmatrix()) if (aperture_size == 3 && !_src.isSubmatrix())
{ {
// Sobel calculation /*
char cvt[2][40]; stage1_with_sobel:
ocl::Kernel calcSobelRowPassKernel("calcSobelRowPass", ocl::imgproc::canny_oclsrc, Sobel operator
format("-D OP_SOBEL -D cn=%d -D shortT=%s -D ucharT=%s" Calc magnitudes
" -D convertToIntT=%s -D intT=%s -D convertToShortT=%s", cn, Non maxima suppression
ocl::typeToStr(CV_16SC(cn)), Double thresholding
ocl::typeToStr(CV_8UC(cn)), */
ocl::convertTypeStr(CV_8U, CV_32S, cn, cvt[0]), char cvt[40];
ocl::typeToStr(CV_32SC(cn)), ocl::Kernel with_sobel("stage1_with_sobel", ocl::imgproc::canny_oclsrc,
ocl::convertTypeStr(CV_32S, CV_16S, cn, cvt[1]))); format("-D WITH_SOBEL -D cn=%d -D TYPE=%s -D convert_intN=%s -D intN=%s -D GRP_SIZEX=%d -D GRP_SIZEY=%d%s",
if (calcSobelRowPassKernel.empty()) cn, ocl::memopTypeToStr(_src.depth()),
ocl::convertTypeStr(_src.type(), CV_32SC(cn), cn, cvt),
ocl::memopTypeToStr(CV_32SC(cn)),
lSizeX, lSizeY,
L2gradient ? " -D L2GRAD" : ""));
if (with_sobel.empty())
return false; return false;
UMat src = _src.getUMat(), dxBuf(size, CV_16SC(cn)), dyBuf(size, CV_16SC(cn)); UMat src = _src.getUMat();
calcSobelRowPassKernel.args(ocl::KernelArg::ReadOnly(src), map.create(size, CV_32S);
ocl::KernelArg::WriteOnlyNoSize(dxBuf), with_sobel.args(ocl::KernelArg::ReadOnly(src),
ocl::KernelArg::WriteOnlyNoSize(dyBuf)); ocl::KernelArg::WriteOnlyNoSize(map),
low, high);
if (!calcSobelRowPassKernel.run(2, globalsize, localsize, false)) size_t globalsize[2] = { size.width, size.height },
return false; localsize[2] = { lSizeX, lSizeY };
// magnitude calculation if (!with_sobel.run(2, globalsize, localsize, false))
ocl::Kernel magnitudeKernel("calcMagnitude_buf", ocl::imgproc::canny_oclsrc,
format("-D cn=%d%s -D OP_MAG_BUF -D shortT=%s -D convertToIntT=%s -D intT=%s",
cn, L2gradient ? " -D L2GRAD" : "",
ocl::typeToStr(CV_16SC(cn)),
ocl::convertTypeStr(CV_16S, CV_32S, cn, cvt[0]),
ocl::typeToStr(CV_32SC(cn))));
if (magnitudeKernel.empty())
return false;
mag = UMat(esize, CV_32SC1, Scalar::all(0));
dx.create(size, CV_16SC(cn));
dy.create(size, CV_16SC(cn));
magnitudeKernel.args(ocl::KernelArg::ReadOnlyNoSize(dxBuf), ocl::KernelArg::ReadOnlyNoSize(dyBuf),
ocl::KernelArg::WriteOnlyNoSize(dx), ocl::KernelArg::WriteOnlyNoSize(dy),
ocl::KernelArg::WriteOnlyNoSize(mag), size.height, size.width);
if (!magnitudeKernel.run(2, globalsize, localsize, false))
return false; return false;
} }
else else
{ {
/*
stage1_without_sobel:
Calc magnitudes
Non maxima suppression
Double thresholding
*/
UMat dx, dy;
Sobel(_src, dx, CV_16S, 1, 0, aperture_size, 1, 0, BORDER_REPLICATE); Sobel(_src, dx, CV_16S, 1, 0, aperture_size, 1, 0, BORDER_REPLICATE);
Sobel(_src, dy, CV_16S, 0, 1, aperture_size, 1, 0, BORDER_REPLICATE); Sobel(_src, dy, CV_16S, 0, 1, aperture_size, 1, 0, BORDER_REPLICATE);
// magnitude calculation ocl::Kernel without_sobel("stage1_without_sobel", ocl::imgproc::canny_oclsrc,
ocl::Kernel magnitudeKernel("calcMagnitude", ocl::imgproc::canny_oclsrc, format("-D WITHOUT_SOBEL -D cn=%d -D GRP_SIZEX=%d -D GRP_SIZEY=%d%s",
format("-D OP_MAG -D cn=%d%s -D intT=int -D shortT=short -D convertToIntT=convert_int_sat", cn, lSizeX, lSizeY, L2gradient ? " -D L2GRAD" : ""));
cn, L2gradient ? " -D L2GRAD" : "")); if (without_sobel.empty())
if (magnitudeKernel.empty())
return false; return false;
mag = UMat(esize, CV_32SC1, Scalar::all(0)); map.create(size, CV_32S);
magnitudeKernel.args(ocl::KernelArg::ReadOnlyNoSize(dx), ocl::KernelArg::ReadOnlyNoSize(dy), without_sobel.args(ocl::KernelArg::ReadOnlyNoSize(dx), ocl::KernelArg::ReadOnlyNoSize(dy),
ocl::KernelArg::WriteOnlyNoSize(mag), size.height, size.width); ocl::KernelArg::WriteOnly(map),
low, high);
if (!magnitudeKernel.run(2, globalsize, NULL, false)) size_t globalsize[2] = { size.width, size.height },
localsize[2] = { lSizeX, lSizeY };
if (!without_sobel.run(2, globalsize, localsize, false))
return false; return false;
} }
// map calculation int PIX_PER_WI = 8;
ocl::Kernel calcMapKernel("calcMap", ocl::imgproc::canny_oclsrc, /*
format("-D OP_MAP -D cn=%d", cn)); stage2:
if (calcMapKernel.empty()) hysteresis (add weak edges if they are connected with strong edges)
*/
ocl::Kernel edgesHysteresis("stage2_hysteresis", ocl::imgproc::canny_oclsrc,
format("-D STAGE2 -D PIX_PER_WI=%d", PIX_PER_WI));
if (edgesHysteresis.empty())
return false; return false;
UMat map(esize, CV_32SC1); edgesHysteresis.args(ocl::KernelArg::ReadWrite(map));
calcMapKernel.args(ocl::KernelArg::ReadOnlyNoSize(dx), ocl::KernelArg::ReadOnlyNoSize(dy),
ocl::KernelArg::ReadOnlyNoSize(mag), ocl::KernelArg::WriteOnlyNoSize(map),
size.height, size.width, low, high);
if (!calcMapKernel.run(2, globalsize, localsize, false)) int sizey = lSizeY / PIX_PER_WI;
if (sizey == 0)
sizey = 1;
size_t globalsize[2] = { size.width, size.height / PIX_PER_WI }, localsize[2] = { lSizeX, sizey };
if (!edgesHysteresis.run(2, globalsize, localsize, false))
return false; return false;
// local hysteresis thresholding
ocl::Kernel edgesHysteresisLocalKernel("edgesHysteresisLocal", ocl::imgproc::canny_oclsrc,
"-D OP_HYST_LOCAL");
if (edgesHysteresisLocalKernel.empty())
return false;
UMat stack(1, size.area(), CV_16UC2), counter(1, 1, CV_32SC1, Scalar::all(0));
edgesHysteresisLocalKernel.args(ocl::KernelArg::ReadOnlyNoSize(map), ocl::KernelArg::PtrReadWrite(stack),
ocl::KernelArg::PtrReadWrite(counter), size.height, size.width);
if (!edgesHysteresisLocalKernel.run(2, globalsize, localsize, false))
return false;
// global hysteresis thresholding
UMat stack2(1, size.area(), CV_16UC2);
int count;
for ( ; ; )
{
ocl::Kernel edgesHysteresisGlobalKernel("edgesHysteresisGlobal", ocl::imgproc::canny_oclsrc,
"-D OP_HYST_GLOBAL");
if (edgesHysteresisGlobalKernel.empty())
return false;
{
Mat _counter = counter.getMat(ACCESS_RW);
count = _counter.at<int>(0, 0);
if (count == 0)
break;
_counter.at<int>(0, 0) = 0;
}
edgesHysteresisGlobalKernel.args(ocl::KernelArg::ReadOnlyNoSize(map), ocl::KernelArg::PtrReadWrite(stack),
ocl::KernelArg::PtrReadWrite(stack2), ocl::KernelArg::PtrReadWrite(counter),
size.height, size.width, count);
#define divUp(total, grain) ((total + grain - 1) / grain)
size_t localsize2[2] = { 128, 1 }, globalsize2[2] = { std::min(count, 65535) * 128, divUp(count, 65535) };
#undef divUp
if (!edgesHysteresisGlobalKernel.run(2, globalsize2, localsize2, false))
return false;
std::swap(stack, stack2);
}
// get edges // get edges
ocl::Kernel getEdgesKernel("getEdges", ocl::imgproc::canny_oclsrc, "-D OP_EDGES");
ocl::Kernel getEdgesKernel("getEdges", ocl::imgproc::canny_oclsrc,
format("-D GET_EDGES -D PIX_PER_WI=%d", PIX_PER_WI));
if (getEdgesKernel.empty()) if (getEdgesKernel.empty())
return false; return false;
_dst.create(size, CV_8UC1); _dst.create(size, CV_8UC1);
UMat dst = _dst.getUMat(); UMat dst = _dst.getUMat();
getEdgesKernel.args(ocl::KernelArg::ReadOnlyNoSize(map), ocl::KernelArg::WriteOnly(dst)); getEdgesKernel.args(ocl::KernelArg::ReadOnly(map), ocl::KernelArg::WriteOnlyNoSize(dst));
return getEdgesKernel.run(2, globalsize, NULL, false); return getEdgesKernel.run(2, globalsize, NULL, false);
} }

View File

@ -43,518 +43,431 @@
// //
//M*/ //M*/
#ifdef OP_SOBEL #define TG22 0.4142135623730950488016887242097f
#define TG67 2.4142135623730950488016887242097f
#if cn != 3 #ifdef WITH_SOBEL
#define loadpix(addr) convertToIntT(*(__global const ucharT *)(addr))
#define storepix(val, addr) *(__global shortT *)(addr) = convertToShortT(val)
#define shortSize (int)sizeof(shortT)
#else
#define loadpix(addr) convertToIntT(vload3(0, (__global const uchar *)(addr)))
#define storepix(val, addr) vstore3(convertToShortT(val), 0, (__global short *)(addr))
#define shortSize (int)sizeof(short) * cn
#endif
// Smoothing perpendicular to the derivative direction with a triangle filter
// only support 3x3 Sobel kernel
// h (-1) = 1, h (0) = 2, h (1) = 1
// h'(-1) = -1, h'(0) = 0, h'(1) = 1
// thus sobel 2D operator can be calculated as:
// h'(x, y) = h'(x)h(y) for x direction
//
// src input 8bit single channel image data
// dx_buf output dx buffer
// dy_buf output dy buffer
__kernel void calcSobelRowPass(__global const uchar * src, int src_step, int src_offset, int rows, int cols,
__global uchar * dx_buf, int dx_buf_step, int dx_buf_offset,
__global uchar * dy_buf, int dy_buf_step, int dy_buf_offset)
{
int gidx = get_global_id(0);
int gidy = get_global_id(1);
int lidx = get_local_id(0);
int lidy = get_local_id(1);
__local intT smem[16][18];
smem[lidy][lidx + 1] = loadpix(src + mad24(src_step, min(gidy, rows - 1), mad24(gidx, cn, src_offset)));
if (lidx == 0)
{
smem[lidy][0] = loadpix(src + mad24(src_step, min(gidy, rows - 1), mad24(max(gidx - 1, 0), cn, src_offset)));
smem[lidy][17] = loadpix(src + mad24(src_step, min(gidy, rows - 1), mad24(min(gidx + 16, cols - 1), cn, src_offset)));
}
barrier(CLK_LOCAL_MEM_FENCE);
if (gidy < rows && gidx < cols)
{
storepix(smem[lidy][lidx + 2] - smem[lidy][lidx],
dx_buf + mad24(gidy, dx_buf_step, mad24(gidx, shortSize, dx_buf_offset)));
storepix(mad24(2, smem[lidy][lidx + 1], smem[lidy][lidx] + smem[lidy][lidx + 2]),
dy_buf + mad24(gidy, dy_buf_step, mad24(gidx, shortSize, dy_buf_offset)));
}
}
#elif defined OP_MAG_BUF || defined OP_MAG
inline intT calc(shortT x, shortT y)
{
#ifdef L2GRAD
intT intx = convertToIntT(x), inty = convertToIntT(y);
return intx * intx + inty * inty;
#else
return convertToIntT( (x >= (shortT)(0) ? x : -x) + (y >= (shortT)(0) ? y : -y) );
#endif
}
#ifdef OP_MAG
// calculate the magnitude of the filter pass combining both x and y directions
// This is the non-buffered version(non-3x3 sobel)
//
// dx_buf dx buffer, calculated from calcSobelRowPass
// dy_buf dy buffer, calculated from calcSobelRowPass
// dx direvitive in x direction output
// dy direvitive in y direction output
// mag magnitude direvitive of xy output
__kernel void calcMagnitude(__global const uchar * dxptr, int dx_step, int dx_offset,
__global const uchar * dyptr, int dy_step, int dy_offset,
__global uchar * magptr, int mag_step, int mag_offset, int rows, int cols)
{
int x = get_global_id(0);
int y = get_global_id(1);
if (y < rows && x < cols)
{
int dx_index = mad24(dx_step, y, mad24(x, (int)sizeof(short) * cn, dx_offset));
int dy_index = mad24(dy_step, y, mad24(x, (int)sizeof(short) * cn, dy_offset));
int mag_index = mad24(mag_step, y + 1, mad24(x + 1, (int)sizeof(int), mag_offset));
__global short * dx = (__global short *)(dxptr + dx_index);
__global short * dy = (__global short *)(dyptr + dy_index);
__global int * mag = (__global int *)(magptr + mag_index);
int cmag = calc(dx[0], dy[0]);
#if cn > 1
short cx = dx[0], cy = dy[0];
int pmag;
#pragma unroll
for (int i = 1; i < cn; ++i)
{
pmag = calc(dx[i], dy[i]);
if (pmag > cmag)
cmag = pmag, cx = dx[i], cy = dy[i];
}
dx[0] = cx, dy[0] = cy;
#endif
mag[0] = cmag;
}
}
#elif defined OP_MAG_BUF
#if cn != 3
#define loadpix(addr) *(__global const shortT *)(addr)
#define shortSize (int)sizeof(shortT)
#else
#define loadpix(addr) vload3(0, (__global const short *)(addr))
#define shortSize (int)sizeof(short)*cn
#endif
// calculate the magnitude of the filter pass combining both x and y directions
// This is the buffered version(3x3 sobel)
//
// dx_buf dx buffer, calculated from calcSobelRowPass
// dy_buf dy buffer, calculated from calcSobelRowPass
// dx direvitive in x direction output
// dy direvitive in y direction output
// mag magnitude direvitive of xy output
__kernel void calcMagnitude_buf(__global const uchar * dx_buf, int dx_buf_step, int dx_buf_offset,
__global const uchar * dy_buf, int dy_buf_step, int dy_buf_offset,
__global uchar * dx, int dx_step, int dx_offset,
__global uchar * dy, int dy_step, int dy_offset,
__global uchar * mag, int mag_step, int mag_offset, int rows, int cols)
{
int gidx = get_global_id(0);
int gidy = get_global_id(1);
int lidx = get_local_id(0);
int lidy = get_local_id(1);
__local shortT sdx[18][16];
__local shortT sdy[18][16];
sdx[lidy + 1][lidx] = loadpix(dx_buf + mad24(min(gidy, rows - 1), dx_buf_step, mad24(gidx, shortSize, dx_buf_offset)));
sdy[lidy + 1][lidx] = loadpix(dy_buf + mad24(min(gidy, rows - 1), dy_buf_step, mad24(gidx, shortSize, dy_buf_offset)));
if (lidy == 0)
{
sdx[0][lidx] = loadpix(dx_buf + mad24(clamp(gidy - 1, 0, rows - 1), dx_buf_step, mad24(gidx, shortSize, dx_buf_offset)));
sdx[17][lidx] = loadpix(dx_buf + mad24(min(gidy + 16, rows - 1), dx_buf_step, mad24(gidx, shortSize, dx_buf_offset)));
sdy[0][lidx] = loadpix(dy_buf + mad24(clamp(gidy - 1, 0, rows - 1), dy_buf_step, mad24(gidx, shortSize, dy_buf_offset)));
sdy[17][lidx] = loadpix(dy_buf + mad24(min(gidy + 16, rows - 1), dy_buf_step, mad24(gidx, shortSize, dy_buf_offset)));
}
barrier(CLK_LOCAL_MEM_FENCE);
if (gidx < cols && gidy < rows)
{
shortT x = sdx[lidy + 1][lidx] * (shortT)(2) + sdx[lidy][lidx] + sdx[lidy + 2][lidx];
shortT y = -sdy[lidy][lidx] + sdy[lidy + 2][lidx];
#if cn == 1 #if cn == 1
*(__global short *)(dx + mad24(gidy, dx_step, mad24(gidx, shortSize, dx_offset))) = x; #define loadpix(addr) convert_intN(*(__global const TYPE *)(addr))
*(__global short *)(dy + mad24(gidy, dy_step, mad24(gidx, shortSize, dy_offset))) = y; #else
#define loadpix(addr) convert_intN(vload3(0, (__global const TYPE *)(addr)))
*(__global int *)(mag + mad24(gidy + 1, mag_step, mad24(gidx + 1, (int)sizeof(int), mag_offset))) = calc(x, y);
#elif cn == 3
intT magv = calc(x, y);
short cx = x.x, cy = y.x;
int cmag = magv.x;
if (cmag < magv.y)
cx = x.y, cy = y.y, cmag = magv.y;
if (cmag < magv.z)
cx = x.z, cy = y.z, cmag = magv.z;
*(__global short *)(dx + mad24(gidy, dx_step, mad24(gidx, shortSize, dx_offset))) = cx;
*(__global short *)(dy + mad24(gidy, dy_step, mad24(gidx, shortSize, dy_offset))) = cy;
*(__global int *)(mag + mad24(gidy + 1, mag_step, mad24(gidx + 1, (int)sizeof(int), mag_offset))) = cmag;
#endif #endif
} #define storepix(value, addr) *(__global int *)(addr) = (int)(value)
}
#endif /*
stage1_with_sobel:
Sobel operator
Calc magnitudes
Non maxima suppression
Double thresholding
*/
#elif defined OP_MAP __constant int prev[4][2] = {
{ 0, -1 },
{ -1, 1 },
{ -1, 0 },
{ -1, -1 }
};
////////////////////////////////////////////////////////////////////////////////////////// __constant int next[4][2] = {
// 0.4142135623730950488016887242097 is tan(22.5) { 0, 1 },
{ 1, -1 },
{ 1, 0 },
{ 1, 1 }
};
#define CANNY_SHIFT 15 inline int3 sobel(int idx, __local const intN *smem)
#define TG22 (int)(0.4142135623730950488016887242097f*(1<<CANNY_SHIFT) + 0.5f)
// First pass of edge detection and non-maximum suppression
// edgetype is set to for each pixel:
// 0 - below low thres, not an edge
// 1 - maybe an edge
// 2 - is an edge, either magnitude is greater than high thres, or
// Given estimates of the image gradients, a search is then carried out
// to determine if the gradient magnitude assumes a local maximum in the gradient direction.
// if the rounded gradient angle is zero degrees (i.e. the edge is in the north-south direction) the point will be considered to be on the edge if its gradient magnitude is greater than the magnitudes in the west and east directions,
// if the rounded gradient angle is 90 degrees (i.e. the edge is in the east-west direction) the point will be considered to be on the edge if its gradient magnitude is greater than the magnitudes in the north and south directions,
// if the rounded gradient angle is 135 degrees (i.e. the edge is in the north east-south west direction) the point will be considered to be on the edge if its gradient magnitude is greater than the magnitudes in the north west and south east directions,
// if the rounded gradient angle is 45 degrees (i.e. the edge is in the north west-south east direction)the point will be considered to be on the edge if its gradient magnitude is greater than the magnitudes in the north east and south west directions.
//
// dx, dy direvitives of x and y direction
// mag magnitudes calculated from calcMagnitude function
// map output containing raw edge types
__kernel void calcMap(__global const uchar * dx, int dx_step, int dx_offset,
__global const uchar * dy, int dy_step, int dy_offset,
__global const uchar * mag, int mag_step, int mag_offset,
__global uchar * map, int map_step, int map_offset,
int rows, int cols, int low_thresh, int high_thresh)
{ {
__local int smem[18][18]; // result: x, y, mag
int3 res;
int gidx = get_global_id(0); intN dx = smem[idx + 2] - smem[idx]
int gidy = get_global_id(1); + 2 * (smem[idx + GRP_SIZEX + 6] - smem[idx + GRP_SIZEX + 4])
+ smem[idx + 2 * GRP_SIZEX + 10] - smem[idx + 2 * GRP_SIZEX + 8];
intN dy = smem[idx] - smem[idx + 2 * GRP_SIZEX + 8]
+ 2 * (smem[idx + 1] - smem[idx + 2 * GRP_SIZEX + 9])
+ smem[idx + 2] - smem[idx + 2 * GRP_SIZEX + 10];
#ifdef L2GRAD
intN magN = dx * dx + dy * dy;
#else
intN magN = convert_intN(abs(dx) + abs(dy));
#endif
#if cn == 1
res.z = magN;
res.x = dx;
res.y = dy;
#else
res.z = max(magN.x, max(magN.y, magN.z));
if (res.z == magN.y)
{
dx.x = dx.y;
dy.x = dy.y;
}
else if (res.z == magN.z)
{
dx.x = dx.z;
dy.x = dy.z;
}
res.x = dx.x;
res.y = dy.x;
#endif
return res;
}
__kernel void stage1_with_sobel(__global const uchar *src, int src_step, int src_offset, int rows, int cols,
__global uchar *map, int map_step, int map_offset,
int low_thr, int high_thr)
{
__local intN smem[(GRP_SIZEX + 4) * (GRP_SIZEY + 4)];
int lidx = get_local_id(0); int lidx = get_local_id(0);
int lidy = get_local_id(1); int lidy = get_local_id(1);
int grp_idx = get_global_id(0) & 0xFFFFF0; int start_x = GRP_SIZEX * get_group_id(0);
int grp_idy = get_global_id(1) & 0xFFFFF0; int start_y = GRP_SIZEY * get_group_id(1);
int tid = mad24(lidy, 16, lidx); int i = lidx + lidy * GRP_SIZEX;
int lx = tid % 18; for (int j = i; j < (GRP_SIZEX + 4) * (GRP_SIZEY + 4); j += GRP_SIZEX * GRP_SIZEY)
int ly = tid / 18;
mag += mag_offset;
if (ly < 14)
smem[ly][lx] = *(__global const int *)(mag +
mad24(mag_step, min(grp_idy + ly, rows - 1), (int)sizeof(int) * (grp_idx + lx)));
if (ly < 4 && grp_idy + ly + 14 <= rows && grp_idx + lx <= cols)
smem[ly + 14][lx] = *(__global const int *)(mag +
mad24(mag_step, min(grp_idy + ly + 14, rows - 1), (int)sizeof(int) * (grp_idx + lx)));
barrier(CLK_LOCAL_MEM_FENCE);
if (gidy < rows && gidx < cols)
{ {
// 0 - the pixel can not belong to an edge int x = clamp(start_x - 2 + (j % (GRP_SIZEX + 4)), 0, cols - 1);
// 1 - the pixel might belong to an edge int y = clamp(start_y - 2 + (j / (GRP_SIZEX + 4)), 0, rows - 1);
// 2 - the pixel does belong to an edge smem[j] = loadpix(src + mad24(y, src_step, mad24(x, cn * (int)sizeof(TYPE), src_offset)));
int edge_type = 0;
int m = smem[lidy + 1][lidx + 1];
if (m > low_thresh)
{
short xs = *(__global const short *)(dx + mad24(gidy, dx_step, mad24(gidx, (int)sizeof(short) * cn, dx_offset)));
short ys = *(__global const short *)(dy + mad24(gidy, dy_step, mad24(gidx, (int)sizeof(short) * cn, dy_offset)));
int x = abs(xs), y = abs(ys);
int tg22x = x * TG22;
y <<= CANNY_SHIFT;
if (y < tg22x)
{
if (m > smem[lidy + 1][lidx] && m >= smem[lidy + 1][lidx + 2])
edge_type = 1 + (int)(m > high_thresh);
} }
else
{
int tg67x = tg22x + (x << (1 + CANNY_SHIFT));
if (y > tg67x)
{
if (m > smem[lidy][lidx + 1]&& m >= smem[lidy + 2][lidx + 1])
edge_type = 1 + (int)(m > high_thresh);
}
else
{
int s = (xs ^ ys) < 0 ? -1 : 1;
if (m > smem[lidy][lidx + 1 - s]&& m > smem[lidy + 2][lidx + 1 + s])
edge_type = 1 + (int)(m > high_thresh);
}
}
}
*(__global int *)(map + mad24(map_step, gidy + 1, mad24(gidx + 1, (int)sizeof(int), + map_offset))) = edge_type;
}
}
#undef CANNY_SHIFT
#undef TG22
#elif defined OP_HYST_LOCAL
struct PtrStepSz
{
__global uchar * ptr;
int step, rows, cols;
};
inline int get(struct PtrStepSz data, int y, int x)
{
return *(__global int *)(data.ptr + mad24(data.step, y + 1, (int)sizeof(int) * (x + 1)));
}
inline void set(struct PtrStepSz data, int y, int x, int value)
{
*(__global int *)(data.ptr + mad24(data.step, y + 1, (int)sizeof(int) * (x + 1))) = value;
}
// perform Hysteresis for pixel whose edge type is 1
//
// If candidate pixel (edge type is 1) has a neighbour pixel (in 3x3 area) with type 2, it is believed to be part of an edge and
// marked as edge. Each thread will iterate for 16 times to connect local edges.
// Candidate pixel being identified as edge will then be tested if there is nearby potiential edge points. If there is, counter will
// be incremented by 1 and the point location is stored. These potiential candidates will be processed further in next kernel.
//
// map raw edge type results calculated from calcMap.
// stack the potiential edge points found in this kernel call
// counter the number of potiential edge points
__kernel void edgesHysteresisLocal(__global uchar * map_ptr, int map_step, int map_offset,
__global ushort2 * st, __global unsigned int * counter,
int rows, int cols)
{
struct PtrStepSz map = { map_ptr + map_offset, map_step, rows + 1, cols + 1 };
__local int smem[18][18];
int2 blockIdx = (int2)(get_group_id(0), get_group_id(1));
int2 blockDim = (int2)(get_local_size(0), get_local_size(1));
int2 threadIdx = (int2)(get_local_id(0), get_local_id(1));
const int x = blockIdx.x * blockDim.x + threadIdx.x;
const int y = blockIdx.y * blockDim.y + threadIdx.y;
smem[threadIdx.y + 1][threadIdx.x + 1] = x < map.cols && y < map.rows ? get(map, y, x) : 0;
if (threadIdx.y == 0)
smem[0][threadIdx.x + 1] = x < map.cols ? get(map, y - 1, x) : 0;
if (threadIdx.y == blockDim.y - 1)
smem[blockDim.y + 1][threadIdx.x + 1] = y + 1 < map.rows ? get(map, y + 1, x) : 0;
if (threadIdx.x == 0)
smem[threadIdx.y + 1][0] = y < map.rows ? get(map, y, x - 1) : 0;
if (threadIdx.x == blockDim.x - 1)
smem[threadIdx.y + 1][blockDim.x + 1] = x + 1 < map.cols && y < map.rows ? get(map, y, x + 1) : 0;
if (threadIdx.x == 0 && threadIdx.y == 0)
smem[0][0] = y > 0 && x > 0 ? get(map, y - 1, x - 1) : 0;
if (threadIdx.x == blockDim.x - 1 && threadIdx.y == 0)
smem[0][blockDim.x + 1] = y > 0 && x + 1 < map.cols ? get(map, y - 1, x + 1) : 0;
if (threadIdx.x == 0 && threadIdx.y == blockDim.y - 1)
smem[blockDim.y + 1][0] = y + 1 < map.rows && x > 0 ? get(map, y + 1, x - 1) : 0;
if (threadIdx.x == blockDim.x - 1 && threadIdx.y == blockDim.y - 1)
smem[blockDim.y + 1][blockDim.x + 1] = y + 1 < map.rows && x + 1 < map.cols ? get(map, y + 1, x + 1) : 0;
barrier(CLK_LOCAL_MEM_FENCE); barrier(CLK_LOCAL_MEM_FENCE);
if (x >= cols || y >= rows) //// Sobel, Magnitude
//
__local int mag[(GRP_SIZEX + 2) * (GRP_SIZEY + 2)];
lidx++;
lidy++;
if (i < GRP_SIZEX + 2)
{
int grp_sizey = min(GRP_SIZEY + 1, rows - start_y);
mag[i] = (sobel(i, smem)).z;
mag[i + grp_sizey * (GRP_SIZEX + 2)] = (sobel(i + grp_sizey * (GRP_SIZEX + 4), smem)).z;
}
if (i < GRP_SIZEY + 2)
{
int grp_sizex = min(GRP_SIZEX + 1, cols - start_x);
mag[i * (GRP_SIZEX + 2)] = (sobel(i * (GRP_SIZEX + 4), smem)).z;
mag[i * (GRP_SIZEX + 2) + grp_sizex] = (sobel(i * (GRP_SIZEX + 4) + grp_sizex, smem)).z;
}
int idx = lidx + lidy * (GRP_SIZEX + 4);
i = lidx + lidy * (GRP_SIZEX + 2);
int3 res = sobel(idx, smem);
mag[i] = res.z;
int x = res.x;
int y = res.y;
barrier(CLK_LOCAL_MEM_FENCE);
//// Threshold + Non maxima suppression
//
/*
Sector numbers
3 2 1
* * *
* * *
0*******0
* * *
* * *
1 2 3
We need to determine arctg(dy / dx) to one of the four directions: 0, 45, 90 or 135 degrees.
Therefore if abs(dy / dx) belongs to the interval
[0, tg(22.5)] -> 0 direction
[tg(22.5), tg(67.5)] -> 1 or 3
[tg(67,5), +oo) -> 2
Since tg(67.5) = 1 / tg(22.5), if we take
a = abs(dy / dx) * tg(22.5) and b = abs(dy / dx) * tg(67.5)
we can get another intervals
in case a:
[0, tg(22.5)^2] -> 0
[tg(22.5)^2, 1] -> 1, 3
[1, +oo) -> 2
in case b:
[0, 1] -> 0
[1, tg(67.5)^2] -> 1,3
[tg(67.5)^2, +oo) -> 2
that can help to find direction without conditions.
0 - might belong to an edge
1 - pixel doesn't belong to an edge
2 - belong to an edge
*/
int gidx = get_global_id(0);
int gidy = get_global_id(1);
if (gidx >= cols || gidy >= rows)
return; return;
int n; int mag0 = mag[i];
int value = 1;
if (mag0 > low_thr)
{
int a = (y / (float)x) * TG22;
int b = (y / (float)x) * TG67;
a = min((int)abs(a), 1) + 1;
b = min((int)abs(b), 1);
// a = { 1, 2 }
// b = { 0, 1 }
// a * b = { 0, 1, 2 } - directions that we need ( + 3 if x ^ y < 0)
int dir3 = (a * b) & (((x ^ y) & 0x80000000) >> 31); // if a = 1, b = 1, dy ^ dx < 0
int dir = a * b + 2 * dir3;
int prev_mag = mag[(lidy + prev[dir][0]) * (GRP_SIZEX + 2) + lidx + prev[dir][1]];
int next_mag = mag[(lidy + next[dir][0]) * (GRP_SIZEX + 2) + lidx + next[dir][1]] + (dir & 1);
if (mag0 > prev_mag && mag0 >= next_mag)
{
value = (mag0 > high_thr) ? 2 : 0;
}
}
storepix(value, map + mad24(gidy, map_step, mad24(gidx, (int)sizeof(int), map_offset)));
}
#elif defined WITHOUT_SOBEL
/*
stage1_without_sobel:
Calc magnitudes
Non maxima suppression
Double thresholding
*/
#define loadpix(addr) (__global short *)(addr)
#define storepix(val, addr) *(__global int *)(addr) = (int)(val)
#ifdef L2GRAD
#define dist(x, y) ((int)(x) * (x) + (int)(y) * (y))
#else
#define dist(x, y) (abs(x) + abs(y))
#endif
__constant int prev[4][2] = {
{ 0, -1 },
{ -1, -1 },
{ -1, 0 },
{ -1, 1 }
};
__constant int next[4][2] = {
{ 0, 1 },
{ 1, 1 },
{ 1, 0 },
{ 1, -1 }
};
__kernel void stage1_without_sobel(__global const uchar *dxptr, int dx_step, int dx_offset,
__global const uchar *dyptr, int dy_step, int dy_offset,
__global uchar *map, int map_step, int map_offset, int rows, int cols,
int low_thr, int high_thr)
{
int start_x = get_group_id(0) * GRP_SIZEX;
int start_y = get_group_id(1) * GRP_SIZEY;
int lidx = get_local_id(0);
int lidy = get_local_id(1);
__local int mag[(GRP_SIZEX + 2) * (GRP_SIZEY + 2)];
__local short2 sigma[(GRP_SIZEX + 2) * (GRP_SIZEY + 2)];
#pragma unroll #pragma unroll
for (int k = 0; k < 16; ++k) for (int i = lidx + lidy * GRP_SIZEX; i < (GRP_SIZEX + 2) * (GRP_SIZEY + 2); i += GRP_SIZEX * GRP_SIZEY)
{ {
n = 0; int x = clamp(start_x - 1 + i % (GRP_SIZEX + 2), 0, cols - 1);
int y = clamp(start_y - 1 + i / (GRP_SIZEX + 2), 0, rows - 1);
if (smem[threadIdx.y + 1][threadIdx.x + 1] == 1) int dx_index = mad24(y, dx_step, mad24(x, cn * (int)sizeof(short), dx_offset));
int dy_index = mad24(y, dy_step, mad24(x, cn * (int)sizeof(short), dy_offset));
__global short *dx = loadpix(dxptr + dx_index);
__global short *dy = loadpix(dyptr + dy_index);
int mag0 = dist(dx[0], dy[0]);
#if cn > 1
short cdx = dx[0], cdy = dy[0];
#pragma unroll
for (int j = 1; j < cn; ++j)
{ {
n += smem[threadIdx.y ][threadIdx.x ] == 2; int mag1 = dist(dx[j], dy[j]);
n += smem[threadIdx.y ][threadIdx.x + 1] == 2; if (mag1 > mag0)
n += smem[threadIdx.y ][threadIdx.x + 2] == 2;
n += smem[threadIdx.y + 1][threadIdx.x ] == 2;
n += smem[threadIdx.y + 1][threadIdx.x + 2] == 2;
n += smem[threadIdx.y + 2][threadIdx.x ] == 2;
n += smem[threadIdx.y + 2][threadIdx.x + 1] == 2;
n += smem[threadIdx.y + 2][threadIdx.x + 2] == 2;
}
if (n > 0)
smem[threadIdx.y + 1][threadIdx.x + 1] = 2;
}
const int e = smem[threadIdx.y + 1][threadIdx.x + 1];
set(map, y, x, e);
n = 0;
if (e == 2)
{ {
n += smem[threadIdx.y ][threadIdx.x ] == 1; mag0 = mag1;
n += smem[threadIdx.y ][threadIdx.x + 1] == 1; cdx = dx[j];
n += smem[threadIdx.y ][threadIdx.x + 2] == 1; cdy = dy[j];
}
n += smem[threadIdx.y + 1][threadIdx.x ] == 1; }
n += smem[threadIdx.y + 1][threadIdx.x + 2] == 1; dx[0] = cdx;
dy[0] = cdy;
n += smem[threadIdx.y + 2][threadIdx.x ] == 1; #endif
n += smem[threadIdx.y + 2][threadIdx.x + 1] == 1; mag[i] = mag0;
n += smem[threadIdx.y + 2][threadIdx.x + 2] == 1; sigma[i] = (short2)(dx[0], dy[0]);
} }
if (n > 0) barrier(CLK_LOCAL_MEM_FENCE);
int gidx = get_global_id(0);
int gidy = get_global_id(1);
if (gidx >= cols || gidy >= rows)
return;
lidx++;
lidy++;
int mag0 = mag[lidx + lidy * (GRP_SIZEX + 2)];
short x = (sigma[lidx + lidy * (GRP_SIZEX + 2)]).x;
short y = (sigma[lidx + lidy * (GRP_SIZEX + 2)]).y;
int value = 1;
if (mag0 > low_thr)
{ {
const int ind = atomic_inc(counter); int a = (y / (float)x) * TG22;
st[ind] = (ushort2)(x + 1, y + 1); int b = (y / (float)x) * TG67;
a = min((int)abs(a), 1) + 1;
b = min((int)abs(b), 1);
int dir3 = (a * b) & (((x ^ y) & 0x80000000) >> 31);
int dir = a * b + 2 * dir3;
int prev_mag = mag[(lidy + prev[dir][0]) * (GRP_SIZEX + 2) + lidx + prev[dir][1]];
int next_mag = mag[(lidy + next[dir][0]) * (GRP_SIZEX + 2) + lidx + next[dir][1]] + (dir & 1);
if (mag0 > prev_mag && mag0 >= next_mag)
{
value = (mag0 > high_thr) ? 2 : 0;
} }
} }
#elif defined OP_HYST_GLOBAL storepix(value, map + mad24(gidy, map_step, mad24(gidx, (int)sizeof(int), map_offset)));
}
__constant int c_dx[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; #undef TG22
__constant int c_dy[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; #undef CANNY_SHIFT
#elif defined STAGE2
/*
stage2:
hysteresis (add edges labeled 0 if they are connected with an edge labeled 2)
*/
#define stack_size 512 #define loadpix(addr) *(__global int *)(addr)
#define map_index mad24(map_step, pos.y, pos.x * (int)sizeof(int)) #define storepix(val, addr) *(__global int *)(addr) = (int)(val)
#define l_stack_size 256
#define p_stack_size 8
__kernel void edgesHysteresisGlobal(__global uchar * map, int map_step, int map_offset, __constant short move_dir[2][8] = {
__global ushort2 * st1, __global ushort2 * st2, __global int * counter, { -1, -1, -1, 0, 0, 1, 1, 1 },
int rows, int cols, int count) { -1, 0, 1, -1, 1, -1, 0, 1 }
};
__kernel void stage2_hysteresis(__global uchar *map, int map_step, int map_offset, int rows, int cols)
{ {
map += map_offset; map += map_offset;
int lidx = get_local_id(0); int x = get_global_id(0);
int y0 = get_global_id(1) * PIX_PER_WI;
int grp_idx = get_group_id(0); int lid = get_local_id(0) + get_local_id(1) * 32;
int grp_idy = get_group_id(1);
__local unsigned int s_counter, s_ind; __local ushort2 l_stack[l_stack_size];
__local ushort2 s_st[stack_size]; __local int l_counter;
if (lidx == 0) if (lid == 0)
s_counter = 0; l_counter = 0;
barrier(CLK_LOCAL_MEM_FENCE); barrier(CLK_LOCAL_MEM_FENCE);
int ind = mad24(grp_idy, (int)get_local_size(0), grp_idx); #pragma unroll
for (int y = y0; y < min(y0 + PIX_PER_WI, rows); ++y)
if (ind < count)
{ {
ushort2 pos = st1[ind]; int type = loadpix(map + mad24(y, map_step, x * (int)sizeof(int)));
if (lidx < 8) if (type == 2)
{ {
pos.x += c_dx[lidx]; l_stack[atomic_inc(&l_counter)] = (ushort2)(x, y);
pos.y += c_dy[lidx];
if (pos.x > 0 && pos.x <= cols && pos.y > 0 && pos.y <= rows && *(__global int *)(map + map_index) == 1)
{
*(__global int *)(map + map_index) = 2;
ind = atomic_inc(&s_counter);
s_st[ind] = pos;
} }
} }
barrier(CLK_LOCAL_MEM_FENCE); barrier(CLK_LOCAL_MEM_FENCE);
while (s_counter > 0 && s_counter <= stack_size - get_local_size(0)) ushort2 p_stack[p_stack_size];
{ int p_counter = 0;
const int subTaskIdx = lidx >> 3;
const int portion = min(s_counter, (uint)(get_local_size(0)>> 3));
if (subTaskIdx < portion) while(l_counter != 0)
pos = s_st[s_counter - 1 - subTaskIdx];
barrier(CLK_LOCAL_MEM_FENCE);
if (lidx == 0)
s_counter -= portion;
barrier(CLK_LOCAL_MEM_FENCE);
if (subTaskIdx < portion)
{ {
pos.x += c_dx[lidx & 7]; int mod = l_counter % 64;
pos.y += c_dy[lidx & 7]; int pix_per_thr = l_counter / 64 + (lid < mod) ? 1 : 0;
if (pos.x > 0 && pos.x <= cols && pos.y > 0 && pos.y <= rows && *(__global int *)(map + map_index) == 1)
#pragma unroll
for (int i = 0; i < pix_per_thr; ++i)
{ {
*(__global int *)(map + map_index) = 2; ushort2 pos = l_stack[ atomic_dec(&l_counter) - 1 ];
ind = atomic_inc(&s_counter);
s_st[ind] = pos; #pragma unroll
for (int j = 0; j < 8; ++j)
{
ushort posx = pos.x + move_dir[0][j];
ushort posy = pos.y + move_dir[1][j];
if (posx < 0 || posy < 0 || posx >= cols || posy >= rows)
continue;
__global uchar *addr = map + mad24(posy, map_step, posx * (int)sizeof(int));
int type = loadpix(addr);
if (type == 0)
{
p_stack[p_counter++] = (ushort2)(posx, posy);
storepix(2, addr);
}
} }
} }
barrier(CLK_LOCAL_MEM_FENCE); barrier(CLK_LOCAL_MEM_FENCE);
}
if (s_counter > 0) while (p_counter > 0)
{ {
if (lidx == 0) l_stack[ atomic_inc(&l_counter) ] = p_stack[--p_counter];
{
ind = atomic_add(counter, s_counter);
s_ind = ind - s_counter;
} }
barrier(CLK_LOCAL_MEM_FENCE); barrier(CLK_LOCAL_MEM_FENCE);
ind = s_ind;
for (int i = lidx; i < (int)s_counter; i += get_local_size(0))
st2[ind + i] = s_st[i];
}
} }
} }
#undef map_index #elif defined GET_EDGES
#undef stack_size
#elif defined OP_EDGES
// Get the edge result. egde type of value 2 will be marked as an edge point and set to 255. Otherwise 0. // Get the edge result. egde type of value 2 will be marked as an edge point and set to 255. Otherwise 0.
// map edge type mappings // map edge type mappings
// dst edge output // dst edge output
__kernel void getEdges(__global const uchar * mapptr, int map_step, int map_offset, __kernel void getEdges(__global const uchar *mapptr, int map_step, int map_offset, int rows, int cols,
__global uchar * dst, int dst_step, int dst_offset, int rows, int cols) __global uchar *dst, int dst_step, int dst_offset)
{ {
int x = get_global_id(0); int x = get_global_id(0);
int y = get_global_id(1); int y0 = get_global_id(1) * PIX_PER_WI;
if (y < rows && x < cols) #pragma unroll
for (int y = y0; y < min(y0 + PIX_PER_WI, rows); ++y)
{ {
int map_index = mad24(map_step, y + 1, mad24(x + 1, (int)sizeof(int), map_offset)); int map_index = mad24(map_step, y, mad24(x, (int)sizeof(int), map_offset));
int dst_index = mad24(dst_step, y, x + dst_offset); int dst_index = mad24(dst_step, y, x) + dst_offset;
__global const int * map = (__global const int *)(mapptr + map_index); __global const int * map = (__global const int *)(mapptr + map_index);
dst[dst_index] = (uchar)(-(map[0] >> 1)); dst[dst_index] = (uchar)(-(map[0] >> 1));
} }
} }