// Copyright (c) 2012-2017 VideoStitch SAS // Copyright (c) 2018 stitchEm // templated kernels: // need to #define kernel names // need to #define xIndex function // to wrap and no-wrap variants // before including this file /** * A kernel that simply adds one to the output pixel for each non-transparent pixel in the input. */ __global__ void countInputsKernel(global_mem uint32_t * __restrict__ dst, unsigned dstWidth, unsigned dstHeight, global_mem const uint32_t * __restrict__ src, unsigned srcWidth, unsigned srcHeight, unsigned offsetX, unsigned offsetY) { const unsigned srcX = get_global_id_x(); const unsigned srcY = get_global_id_y(); const unsigned dstX = xIndex(srcX + offsetX, dstWidth); const unsigned dstY = srcY + offsetY; if (srcX < srcWidth && srcY < srcHeight && dstX < dstWidth && dstY < dstHeight) { if (Image_RGBA_a(src[srcWidth * srcY + srcX])) { ++(dst[dstY * dstWidth + dstX]); } } } /** * A kernel that computes the stitching error. It ignores pixels with zero alpha and alreday processed pixels. * Already processed pixels are identified by a mask 0x80000000. */ __global__ void stitchingErrorKernel(global_mem uint32_t * __restrict__ dst, unsigned dstWidth, unsigned dstHeight, global_mem const uint32_t * __restrict__ src, unsigned srcWidth, unsigned srcHeight, unsigned offsetX, unsigned offsetY) { const unsigned srcX = get_global_id_x(); const unsigned srcY = get_global_id_y(); const unsigned dstX = xIndex(srcX + offsetX, dstWidth); const unsigned dstY = srcY + offsetY; if (srcX < srcWidth && srcY < srcHeight && dstX < dstWidth && dstY < dstHeight) { const uint32_t inSrc = src[srcWidth * srcY + srcX]; // Only consider solid pixels. if (Image_RGBA_a(inSrc)) { const uint32_t inDst = dst[dstY * dstWidth + dstX]; // First case: the destination pixel is already processed => skip. if (!(inDst & 0x80000000)) { // Second case: the destination pixel has not yet been seen, just write it. if (!Image_RGBA_a(inDst)) { dst[dstY * dstWidth + dstX] = 0x40ffffff & inSrc; } else { const int32_t rSrc = Image_RGBA_r(inSrc); const int32_t rDst = Image_RGBA_r(inDst); const int32_t gSrc = Image_RGBA_g(inSrc); const int32_t gDst = Image_RGBA_g(inDst); const int32_t bSrc = Image_RGBA_b(inSrc); const int32_t bDst = Image_RGBA_b(inDst); dst[dstY * dstWidth + dstX] = 0xc0000000 | ((rSrc - rDst) * (rSrc - rDst) + (gSrc - gDst) * (gSrc - gDst) + (bSrc - bDst) * (bSrc - bDst)); } } } } } /** * A kernel that computes the exposure RGB diff by channel. It ignores pixels with zero alpha and alreday processed pixels. * Already processed pixels are identified by an alpha mask 0x02. */ __global__ void exposureErrorRGBKernel(global_mem uint32_t * __restrict__ dst, unsigned dstWidth, unsigned dstHeight, global_mem const uint32_t * __restrict__ src, unsigned srcWidth, unsigned srcHeight, unsigned offsetX, unsigned offsetY) { const unsigned srcX = get_global_id_x(); const unsigned srcY = get_global_id_y(); const unsigned dstX = xIndex(srcX + offsetX, dstWidth); const unsigned dstY = srcY + offsetY; if (srcX < srcWidth && srcY < srcHeight && dstX < dstWidth && dstY < dstHeight) { const uint32_t inSrc = src[srcWidth * srcY + srcX]; // Only consider solid pixels if (Image_RGBA_a(inSrc)) { const uint32_t inDst = dst[dstY * dstWidth + dstX]; // The destination pixel is already processed => skip if (!(Image_RGBA_a(inDst) & 0x2)) { // The destination pixel has not yet been seen, just write it. if (!Image_RGBA_a(inDst)) { dst[dstY * dstWidth + dstX] = 0x01ffffff & inSrc; } else { const uint32_t rDiff = abs((int32_t)Image_RGBA_r(inSrc) - (int32_t)Image_RGBA_r(inDst)); const uint32_t gDiff = abs((int32_t)Image_RGBA_g(inSrc) - (int32_t)Image_RGBA_g(inDst)); const uint32_t bDiff = abs((int32_t)Image_RGBA_b(inSrc) - (int32_t)Image_RGBA_b(inDst)); dst[dstY * dstWidth + dstX] = Image_RGBA_pack(rDiff, gDiff, bDiff, 0x3); } } } } } /** * A kernel that creates a checkerboard pattern in overlapping areas, between first and second input. * The first arriving value for any coordinate is always written. It get an alpha value of 0x01. * If a second value is available, it replaces the first value on "black" squares, * while on "white" squares the first value is kept. The second value gets an alpha value of 0x03. * Further values are ignored. */ __global__ void checkerInsertKernel(global_mem uint32_t * __restrict__ dst, unsigned dstWidth, unsigned dstHeight, global_mem const uint32_t * __restrict__ src, unsigned srcWidth, unsigned srcHeight, unsigned offsetX, unsigned offsetY, unsigned checkerSize) { const unsigned srcX = get_global_id_x(); const unsigned srcY = get_global_id_y(); const unsigned dstX = xIndex(srcX + offsetX, dstWidth); const unsigned dstY = srcY + offsetY; if (srcX < srcWidth && srcY < srcHeight && dstX < dstWidth && dstY < dstHeight) { const uint32_t inSrc = src[srcWidth * srcY + srcX]; // Only consider solid pixels if (Image_RGBA_a(inSrc)) { const uint32_t inDst = dst[dstY * dstWidth + dstX]; // The destination pixel is already processed => skip if (!(Image_RGBA_a(inDst) & 0x2)) { // The destination pixel has not yet been seen, just write it. if (!(Image_RGBA_a(inDst) & 0x1)) { dst[dstY * dstWidth + dstX] = 0x01ffffff & inSrc; } else { bool useSource = true; if (checkerSize > 1) { unsigned evenRow = ((dstX / checkerSize) & 1); unsigned evenCol = ((dstY / checkerSize) & 1); useSource = evenRow ^ evenCol; } if (useSource) { dst[dstY * dstWidth + dstX] = 0x03ffffff & inSrc; } else { dst[dstY * dstWidth + dstX] = 0x03ffffff & inDst; } } } } } } /** * A kernel that store invidiual input with maps to the given panorama coordinate. * It ignores pixels with zero alpha and alreday processed pixels. * For each input, the warped pixel is tranformed to grayscale. * The first input which maps to a given panorama pixel will be stored in the R component. * The second input which maps to the same panorama pixel will be stored in the G component. * The alpha bit is set to 1 if and only if two inputs map to a panorama pixel. * The B component is used as a counter (0: no mapped input for this pixel, 1: 1 mapped input, 2: one or more mapped inputs) */ __global__ void noblendKernel(global_mem uint32_t * __restrict__ dst, unsigned dstWidth, unsigned dstHeight, global_mem const uint32_t * __restrict__ src, unsigned srcWidth, unsigned srcHeight, unsigned offsetX, unsigned offsetY) { const unsigned srcX = get_global_id_x(); const unsigned srcY = get_global_id_y(); const unsigned dstX = xIndex(srcX + offsetX, dstWidth); const unsigned dstY = srcY + offsetY; if (srcX < srcWidth && srcY < srcHeight && dstX < dstWidth && dstY < dstHeight) { const uint32_t inSrc = src[srcWidth * srcY + srcX]; int ia = Image_RGBA_a(inSrc); // Only consider solid pixels. if (ia) { int ir = Image_RGBA_r(inSrc); int ig = Image_RGBA_g(inSrc); int ib = Image_RGBA_b(inSrc); const uint32_t inDst = dst[dstY * dstWidth + dstX]; int gray = 299 * ir + 587 * ig + 114 * ib; gray = gray / 1000; int outa = Image_RGBA_a(inDst); int outr = Image_RGBA_r(inDst); int outg = Image_RGBA_g(inDst); int outb = Image_RGBA_b(inDst); if (outb == 0) { outb = 1; outr = gray; } else if (outb == 1) { outb = 2; outg = gray; outa = 1; } dst[dstY * dstWidth + dstX] = Image_RGBA_pack(outr, outg, outb, outa); } } }