【问题标题】:Image Processing: Image has grid lines after applying filter图像处理:应用滤镜后图像有网格线
【发布时间】:2014-12-18 22:53:19
【问题描述】:

我对低级别的图像处理非常陌生,并且刚刚尝试使用 GPU 和 CPU 实现高斯内核 - 但是两者都产生相同的输出,图像被网格严重倾斜:

我知道我可以使用 OpenCV 的预构建函数来处理过滤器,但我想了解它背后的方法,所以我自己构建了。

卷积核:

// Convolution kernel - this manipulates the given channel and writes out a new blurred channel.
void convoluteChannel_cpu(
                        const unsigned char* const channel,         // Input channel
                        unsigned char* const channelBlurred,        // Output channel
                        const size_t numRows, const size_t numCols, // Channel width/height (rows, cols)
                        const float *filter,                        // The weight of sigma, to convulge
                        const int filterWidth                       // This is normally a sample of 9
                     )
{
    // Loop through the images given R, G or B channel
    for(int rows = 0; rows < (int)numRows; rows++)
    {
        for(int cols = 0; cols < (int)numCols; cols++)
        {
            // Declare new pixel colour value
            float newColor = 0.f;

            // Loop for every row along the stencil size (3x3 matrix)
            for(int filter_x = -filterWidth/2; filter_x <= filterWidth/2; filter_x++)
            {
                // Loop for every col along the stencil size (3x3 matrix)
                for(int filter_y = -filterWidth/2; filter_y <= filterWidth/2; filter_y++)
                {
                    // Clamp to the boundary of the image to ensure we don't access a null index.
                    int image_x = __min(__max(rows + filter_x, 0), static_cast<int>(numRows -1));
                    int image_y = __min(__max(cols + filter_y, 0), static_cast<int>(numCols -1));

                    // Assign the new pixel value to the current pixel, numCols and numRows are both 3, so we only 
                    // need to use one to find the current pixel index (similar to how we find the thread in a block)
                    float pixel = static_cast<float>(channel[image_x * numCols + image_y]);

                    // Sigma is the new weight to apply to the image, we perform the equation to get a radnom weighting,
                    // if we don't do this the image will become choppy.
                    float sigma = filter[(filter_x + filterWidth / 2) * filterWidth + filter_y + filterWidth/2];
                    //float sigma = 1 / 81.f;

                    // Set the new pixel value
                    newColor += pixel * sigma;
                }
            }

            // Set the value of the next pixel at the current image index with the newly declared color
            channelBlurred[rows * numCols + cols] = newColor;
        }
    }
}

我从另一种方法中调用了 3 次,该方法将图像分成各自的 R、G、B 通道,但我不相信这会导致图像发生如此严重的变异。

以前有没有人遇到过类似的问题,如果有,你是怎么解决的?

编辑频道拆分功能:

void gaussian_cpu(
                    const uchar4* const rgbaImage,       // Our input image from the camera
                    uchar4* const outputImage,           // The image we are writing back for display
                    size_t numRows, size_t numCols,      // Width and Height of the input image (rows/cols)
                    const float* const filter,           // The value of sigma
                    const int filterWidth                // The size of the stencil (3x3) 9
                 )
{
    // Build an array to hold each channel for the given image
    unsigned char *r_c = new unsigned char[numRows * numCols];
    unsigned char *g_c = new unsigned char[numRows * numCols];
    unsigned char *b_c = new unsigned char[numRows * numCols];

    // Build arrays for each of the output (blurred) channels
    unsigned char *r_bc = new unsigned char[numRows * numCols]; 
    unsigned char *g_bc = new unsigned char[numRows * numCols]; 
    unsigned char *b_bc = new unsigned char[numRows * numCols]; 

    // Separate the image into R,G,B channels
    for(size_t i = 0; i < numRows * numCols; i++) 
    {
        uchar4 rgba = rgbaImage[i];
        r_c[i] = rgba.x;
        g_c[i] = rgba.y;
        b_c[i] = rgba.z;
    }

    // Convolute each of the channels using our array
    convoluteChannel_cpu(r_c, r_bc, numRows, numCols, filter, filterWidth);
    convoluteChannel_cpu(g_c, g_bc, numRows, numCols, filter, filterWidth);
    convoluteChannel_cpu(b_c, b_bc, numRows, numCols, filter, filterWidth);

    // Recombine the channels to build the output image - 255 for alpha as we want 0 transparency
    for(size_t i = 0; i < numRows * numCols; i++) 
    {
        uchar4 rgba = make_uchar4(r_bc[i], g_bc[i], b_bc[i], 255);
        outputImage[i] = rgba;
    }
}

编辑调用内核

while(gpu_frames > 0)
{
    //cout << gpu_frames << "\n";
    camera >> frameIn;

    // Allocate I/O Pointers
    beginStream(&h_inputFrame, &h_outputFrame, &d_inputFrame, &d_outputFrame, &d_redBlurred, &d_greenBlurred, &d_blueBlurred, &_h_filter, &filterWidth, frameIn);

    // Show the source image
    imshow("Source", frameIn);

    g_timer.Start();
    // Allocate mem to GPU
    allocateMemoryAndCopyToGPU(numRows(), numCols(), _h_filter, filterWidth);

    // Apply the gaussian kernel filter and then free any memory ready for the next iteration
    gaussian_gpu(h_inputFrame, d_inputFrame, d_outputFrame, numRows(), numCols(), d_redBlurred, d_greenBlurred, d_blueBlurred, filterWidth);

    // Output the blurred image
    cudaMemcpy(h_outputFrame, d_frameOut, sizeof(uchar4) * numPixels(), cudaMemcpyDeviceToHost);
    g_timer.Stop();
    cudaDeviceSynchronize();
    gpuTime += g_timer.Elapsed();
    cout << "Time for this kernel " << g_timer.Elapsed() << "\n";

    Mat outputFrame(Size(numCols(), numRows()), CV_8UC1, h_outputFrame, Mat::AUTO_STEP);

    clean_mem();

    imshow("Dest", outputFrame);

    // 1ms delay to prevent system from being interrupted whilst drawing the new frame
    waitKey(1);
    gpu_frames--;
}

然后在 beginStream() 方法中,将图像转换为 uchar4:

 // Allocate host variables, casting the frameIn and frameOut vars to uchar4 elements, these will
 // later be processed by the kernel
*h_inputFrame = (uchar4 *)frameIn.ptr<unsigned char>(0);
*h_outputFrame = (uchar4 *)frameOut.ptr<unsigned char>(0);

【问题讨论】:

  • 我认为您没有考虑到输入图像是 RGB 并且具有 3 通道。如果您的算法输出的是 1 通道图像,最好在执行算法之前将输入图像转换为单通道。
  • 这种毛刺似乎和输入/输出通道数有关。
  • 嗯,我的解决方案中是否有任何明显的东西可能会扭曲输出?
  • 改变你的算法来执行灰度转换,你会看到。
  • @berak 过滤是在全长中完成的。 (-x 到 x),其中 x 是宽度/2。

标签: opencv image-processing


【解决方案1】:

这个问题有很多疑问。 在代码的开头,它提到过滤器宽度为 9,因此使其成为 9x9 内核。但在其他一些 cmets 中,它说是 3。所以我猜你实际上使用的是 9x9 内核,并且过滤器中确实有 81 个权重。

但上面的输出永远不可能是由于上面提到的混淆。

uchar4 大小为 4 字节。因此,在 gaussian_cpu 中,通过在不包含 alpha 值的图像上运行 rgbaImage[i] 循环来拆分数据(可以从上述循环中推断出 alpha 不存在),实际完成的是您正在复制 R1 ,G2,B3,R5,G6,B7 等到红色通道。最好您最初在灰度图像上尝试代码,并确保您使用的是 uchar 而不是 uchar4。

输出图像看起来正好是原始图像宽度的 1/3,这使得上述假设成立。

编辑 1:

guassian_cpu 函数的输入 rgbaImage 是 RGBA 还是 RGB? videoCapture 必须提供 3 通道输出。 *h_inputFrame(到 uchar4)的初始化本身是错误的,因为它指向 3 通道数据。 类似地,输出数据是四通道数据,但Mat outputFrame 被声明为指向这四通道数据的单通道。尝试将 Mat outputFrame 设置为 8UC3 type 并查看结果。

另外,代码是如何工作的,guassian_cpu() 函数在定义中有 7 个输入参数,但是当您调用该函数时,使用了 8 个参数。希望这只是一个错字。

【讨论】:

  • 抱歉,为了消除混淆,它是一个 3x3 过滤器,我在评论上放 9 的唯一原因是提醒我乘法后的总大小
  • 你说的有道理,但是当我用 gaussiancpu 方法拍摄图像并分割通道时,我将 255 放入 alpha 中。也许我应该放弃它而只使用 uchar ?
  • 至于内核被错误调用,我刚刚意识到我发布了 GPU 内核调用而不是 CPU - 我可以向您保证参数匹配:P - beginStream() 函数转换从相机捕获的初始 BGR 图像转换为 RGBA,然后将其转换为 uchar4,将其写回 h_output 指针,为内核做准备。
  • 如果是这样,那么"Mat outputFrame(Size(numCols(), numRows()), CV_8UC1, ...)" 被修改为CV_8UC4并且代码必须工作
  • @Alex..尝试在 camera&gt;&gt;frameIn 之后使用 cv::cvtColor(frameIn,frameIn,cv::COLOR_BGR2BGRA) 将您的输入图像转换为实际的 4 通道图像。您的 gaussian_cpu 函数工作正常,只需为其提供正确分配的输入和输出图像。
猜你喜欢
  • 2021-06-27
  • 1970-01-01
  • 1970-01-01
  • 2012-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-04
  • 2015-01-29
相关资源
最近更新 更多