这个伪代码不会给出一些大于 255 的图像值吗?

Wouldn't this pseudocode give some image values greater than 255?

我正在根据以下摘自 Wikipedia 的伪代码实现 sobel 过滤器:

function sobel(A : as two dimensional image array)
    Gx=[-1 0 1; -2 0 2; -1 0 1]
    Gy=[-1 -2 -1; 0 0 0; 1 2 1]

    rows = size(A,1)
    columns = size(A,2)
    mag=zeros(A)

    for i=1:rows-2
        for j=1:columns-2
            S1=sum(sum(Gx.*A(i:i+2,j:j+2)))
            S2=sum(sum(Gy.*A(i:i+2,j:j+2)))

            mag(i+1,j+1)=sqrt(S1.^2+S2.^2)
        end for
    end for

    threshold = 70 %varies for application [0 255]
    output_image = max(mag,threshold)
    output_image(output_image==round(threshold))=0;
    return output_image
end function

但是,在应用此算法后,我得到许多 output_image 高于 255 的值,考虑到 Gx 和 Gy 的定义方式,这是有道理的。我怎样才能修改这个算法,使值不超过 255,最后结果看起来更像这样?:

--- 编辑 ---

我的过滤器实现中出现了一些错误,我认为这就是为什么值高于 255 的原因。修复错误后,值的范围在 0 - 16 之间。因为现在所有值都低于 70,应用 70 的阈值将所有内容发送到 0。所以我设置了一个较低的阈值 5,并将其余值乘以 10(以增强边缘,因为它们在 5-16 范围内)并得到以下结果:

我也尝试了评论中提到的归一化方法,但得到了类似的噪点图像。

--- 编辑 2 ---

由于请求了实际代码,所以我将代码发布在 Halide.

int main(int argc, char **argv) {
    Var x, y, k, c;
    Buffer<uint8_t> left_buffer = load_image("images/stereo/bike.jpg");
    Expr clamped_x = clamp(x, 0, left_buffer.width() - 1);
    Expr clamped_y = clamp(y, 0, left_buffer.height() - 1);
    Func left_original("left_original");
    left_original(x, y) = left_buffer(clamped_x, clamped_y);
    left_original.compute_root();

    // 3x3 sobel filter
    Buffer<uint8_t> sobel_1(3);
    sobel_1(0) = -1;
    sobel_1(1) = 0;
    sobel_1(2) = 1;

    Buffer<uint8_t> sobel_2(3);
    sobel_2(0) = 1;
    sobel_2(1) = 2;
    sobel_2(2) = 1;


    RDom conv_x(-1, 2);
    RDom conv_y(-1, 2);

    Func output_x_inter("output_x_inter");
    output_x_inter(x, y) = sum(left_original(x - conv_x, y) * sobel_1(conv_x + 1));
    output_x_inter.compute_root();
    Func output_x("output_x");
    output_x(x, y) = sum(output_x_inter(x, y - conv_y) * sobel_2(conv_y + 1));
    output_x.compute_root();

    Func output_y("output_y");
    output_y(x, y) = sum(conv_y, sum(conv_x, left_original(x - conv_x, y - conv_y) * sobel_2(conv_x + 1)) * sobel_1(conv_y + 1));
    output_y.compute_root();

    Func output("output");
    output(x, y) = sqrt(output_x(x, y) * output_x(x, y) + output_y(x, y) * output_y(x, y));
    output.compute_root();
    output.trace_stores();


    RDom img(0, left_buffer.width(), 0, left_buffer.height());

    Func max("max");
    max(k) = f32(0);
    max(0) = maximum(output(img.x, img.y));
    max.compute_root();
    Func min("min");
    min(k) = f32(0);
    min(0) = minimum(output(img.x, img.y));
    min.compute_root();

    Func output_u8("output_u8");
    // The following line sends all the values of output <= 5 to zero, and multiplies the resulting values by 10 to enhance the intensity of the edges.
    output_u8(x, y) = u8(select(output(x, y) <= 5, 0, output(x, y))*10);
    output_u8.compute_root();
    output_u8.trace_stores();



    Buffer<uint8_t> output_buff = output_u8.realize(left_buffer.width(), left_buffer.height());
    save_image(output_buff, "images/stereo/sobel/out.png");



}

--- 编辑 3 ---

正如一个答案所建议的,我将所有类型都更改为 float,除了最后一个,它必须是无符号 8 位类型。这是代码,以及我得到的结果。


int main(int argc, char **argv) {
    Var x, y, k, c;
    Buffer<uint8_t> left_buffer = load_image("images/stereo/bike.jpg");
    Expr clamped_x = clamp(x, 0, left_buffer.width() - 1);
    Expr clamped_y = clamp(y, 0, left_buffer.height() - 1);
    Func left_original("left_original");
    left_original(x, y) = left_buffer(clamped_x, clamped_y);
    left_original.compute_root();

    // 3x3 sobel filter
    Buffer<float_t> sobel_1(3);
    sobel_1(0) = -1;
    sobel_1(1) = 0;
    sobel_1(2) = 1;

    Buffer<float_t> sobel_2(3);
    sobel_2(0) = 1;
    sobel_2(1) = 2;
    sobel_2(2) = 1;


    RDom conv_x(-1, 2);
    RDom conv_y(-1, 2);

    Func output_x_inter("output_x_inter");
    output_x_inter(x, y) = f32(sum(left_original(x - conv_x, y) * sobel_1(conv_x + 1)));
    output_x_inter.compute_root();
    Func output_x("output_x");
    output_x(x, y) = f32(sum(output_x_inter(x, y - conv_y) * sobel_2(conv_y + 1)));
    output_x.compute_root();
    RDom img(0, left_buffer.width(), 0, left_buffer.height());

    Func output_y("output_y");
    output_y(x, y) = f32(sum(conv_y, sum(conv_x, left_original(x - conv_x, y - conv_y) * sobel_2(conv_x + 1)) * sobel_1(conv_y + 1)));
    output_y.compute_root();

    Func output("output");
    output(x, y) = sqrt(output_x(x, y) * output_x(x, y) + output_y(x, y) * output_y(x, y));
    output.compute_root();

    Func max("max");
    max(k) = f32(0);
    max(0) = maximum(output(img.x, img.y));
    max.compute_root();
    Func min("min");
    min(k) = f32(0);
    min(0) = minimum(output(img.x, img.y));
    min.compute_root();

    // output_inter for scaling 
    Func output_inter("output_inter");
    output_inter(x, y) = f32((output(x, y) - min(0)) * 255 / (max(0) - min(0)));
    output_inter.compute_root();

    Func output_u8("output_u8");
    output_u8(x, y) = u8(select(output_inter(x, y) <= 70, 0, output_inter(x, y)));
    output_u8.compute_root();
    output_u8.trace_stores();


    Buffer<uint8_t> output_buff = output_u8.realize(left_buffer.width(), left_buffer.height());
    save_image(output_buff, "images/stereo/sobel/out.png");

}

--- 编辑 4 ---

正如@CrisLuengo 所建议的,我简化了我的代码并输出了以下结果:

output(x, y) = u8(min(sqrt(output_x(x, y) * output_x(x, y) + output_y(x, y) * output_y(x, y)), 255));

由于许多值远高于 255,因此这些值被限制在 255,因此我们得到 "washed out" 图像:

我不知道 Halide 语法,我刚知道它存在。但我可以指出一个明显的问题:

Buffer<uint8_t> sobel_1(3);
sobel_1(0) = -1;

您正在将 -1 分配给 uint8 类型。这没有按预期工作。使内核成为 float,并将所有计算作为浮点数进行,然后缩放结果并将其存储在 uint8 输出图像中。

在使用小整数类型进行计算时,必须非常小心上溢和下溢。 Sobel 计算可能会在(带符号的)int16 类型中完成,但根据我的经验,与使用 float 类型,然后缩放(或钳位)并将结果转换为相比,没有任何优势输出图像的类型。

我终于弄明白了,但我不确定为什么 Halide 会这样。 当我这样做时:

RDom conv_x(-1, 2);
RDom conv_y(-1, 2);
Func output_x_inter("output_x_inter");
output_x_inter(x, y) = f32(sum(left_original(x - conv_x, y) * sobel_1(conv_x + 1)));
Func output_x("output_x");
output_x(x, y) = f32(sum(output_x_inter(x, y - conv_y) * sobel_2(conv_y + 1)));

事情不奏效。但是当我 "unroll" 求和函数工作时:

Func output_x_inter("output_x_inter");
output_x_inter(x, y) = f32(left_original(x + 1, y) * sobel_1(0) + left_original(x, y) * sobel_1(1) + left_original(x - 1, y) * sobel_1(2));
Func output_x("output_x");
output_x(x, y) = f32(output_x_inter(x, y + 1) * sobel_2(0) + output_x_inter(x,  y) * sobel_2(1) + output_x_inter(x, y - 1) * sobel_2(2));