OpenGL GLSL - Bộ lọc phát hiện cạnh Sobel


9

Đối với chủ đề này, tôi đã triển khai thành công bộ lọc Phát hiện cạnh Sobel trong GLSL. Đây là mã shader mảnh của bộ lọc:

#version 330 core
in vec2 TexCoords;
out vec4 color;

uniform sampler2D screenTexture;

mat3 sx = mat3( 
    1.0, 2.0, 1.0, 
    0.0, 0.0, 0.0, 
   -1.0, -2.0, -1.0 
);
mat3 sy = mat3( 
    1.0, 0.0, -1.0, 
    2.0, 0.0, -2.0, 
    1.0, 0.0, -1.0 
);

void main()
{
    vec3 diffuse = texture(screenTexture, TexCoords.st).rgb;
    mat3 I;
    for (int i=0; i<3; i++) {
        for (int j=0; j<3; j++) {
            vec3 sample  = texelFetch(screenTexture, ivec2(gl_FragCoord) + ivec2(i-1,j-1), 0 ).rgb;
            I[i][j] = length(sample); 
    }
}

float gx = dot(sx[0], I[0]) + dot(sx[1], I[1]) + dot(sx[2], I[2]); 
float gy = dot(sy[0], I[0]) + dot(sy[1], I[1]) + dot(sy[2], I[2]);

float g = sqrt(pow(gx, 2.0)+pow(gy, 2.0));
color = vec4(diffuse - vec3(g), 1.0);
} 

Và đây là kết quả của một khối lập phương với phát hiện cạnh Sobel:

Cube với bộ lọc phát hiện cạnh Sobel

Nếu bạn phóng to hình ảnh, bạn sẽ thấy có rất nhiều "nhiễu" do Sobel tạo ra: Có các sọc ngang màu xám trên toàn cảnh do độ dốc màu xanh / trắng. Hơn nữa, các nón ánh sáng tạo ra một mô hình không mong muốn trên khối lập phương. Các cạnh màu đen bên trái của khối lập phương dường như cũng mờ dần vì hình nón nhẹ ở nửa bên trái của khối lập phương.

Vì vậy, tôi đã đọc bài viết này trong đó tuyên bố rằng người ta nên tô màu cho hình ảnh trước và sử dụng bộ lọc mờ Gaussian để làm cho các cạnh rõ ràng hơn. Ở dưới cùng của bài viết, cũng có bộ lọc phát hiện cạnh canny dường như tạo ra kết quả tốt hơn.

Bây giờ tôi có hai câu hỏi:

  1. Các bước sau đây có đúng không để tạo ra kết quả phát hiện cạnh tốt nhất có thể:

    • Thang độ xám
    • Gaussian Blur
    • Phát hiện cạnh Sobel / Canny
  2. Nếu có, làm cách nào để hợp nhất ảnh gốc với ảnh đã xử lý? Ý tôi là, sau khi xử lý các bước đã nêu ở trên, tôi nhận được một hình ảnh hoàn toàn màu đen với các cạnh trắng hoặc ngược lại. Làm thế nào tôi có thể đặt các cạnh trên hình ảnh / kết cấu ban đầu của tôi?

Cảm ơn bạn đã giúp đỡ!


Tôi tự hỏi nếu bạn có thể nhận được kết quả bạn muốn bằng cách sử dụng cờ cạnh của OpenGL theo một cách nào đó. Dường như chỉ có các cạnh giữa các đỉnh với cờ cạnh được vẽ khi ở chế độ đường. Có một mô tả ở đây . (Tôi đã không sử dụng bản thân mình hoặc tôi sẽ đăng một ví dụ.)
user1118321

Câu trả lời:


9
  1. Các kết quả tốt nhất phụ thuộc mạnh mẽ vào trường hợp sử dụng của bạn. Họ cũng phụ thuộc vào hiệu ứng bạn muốn đạt được. Sobel chỉ là một bộ lọc phát hiện cạnh: các cạnh sẽ phụ thuộc vào tín hiệu đầu vào, việc chọn tín hiệu đầu vào đó là tùy thuộc vào bạn.

    Ở đây bạn đang sử dụng hình ảnh màu làm đầu vào và bộ lọc phát hiện chính xác các cạnh mờ trong gradient màu xanh lam, trong khi các cạnh của khối bị gián đoạn khi màu của nó quá gần màu nền.

    Vì tôi cho rằng chương trình của bạn cũng chịu trách nhiệm vẽ khối, bạn có quyền truy cập vào thông tin khác mà bạn có thể cung cấp cho bộ lọc Sobel của mình. Ví dụ độ sâu và quy tắc là ứng cử viên tốt để phát hiện cạnh. Các albedo trước khi chiếu sáng có thể được sử dụng để. Kiểm tra với các đầu vào khác nhau và quyết định sử dụng cái nào tùy thuộc vào kết quả bạn nhận được.

  2. Về câu hỏi của bạn về cách kết hợp thông tin cạnh, tôi khuyên bạn nên lọc gmột chút trước khi sử dụng. Sau đó, bạn có thể sử dụng nó để nội suy giữa màu gốc và màu cạnh mong muốn.

    Ví dụ, bạn có thể thử một cái gì đó như thế này:

    float g = sqrt(pow(gx, 2.0)+pow(gy, 2.0));

    // Try different values and see what happens
    g = smoothstep(0.4, 0.6, g);

    vec3 edgeColor = vec3(1., 0., 0.2);
    color = vec4(mix(diffuse, edgeColor, g), 1.);

Phụ lục

Để sử dụng độ sâu hoặc quy tắc, bạn sẽ cần lưu chúng trong một kết cấu nếu điều đó chưa được thực hiện. Khi bạn tạo bộ đệm khung cho thẻ kết xuất thông thường của mình, bạn có thể đính kèm nhiều họa tiết khác nhau vào nó (xem glFramebufferTexture2D) và viết thông tin khác cho chúng chứ không chỉ là màu của cảnh.

Nếu bạn đính kèm một kết cấu độ sâu (với GL_DEPTH_ATTACHMENT), nó sẽ được sử dụng tự động cho độ sâu. Nếu bạn đính kèm một hoặc một vài họa tiết màu (với GL_COLOR_ATTACHMENTi), bạn có thể viết cho chúng bằng cách khai báo một số kết quả đầu ra cho các shader mảnh của bạn (điều này thường được thực hiện với gl_FragData; một trong hai cách, xem glDrawBuffers).

Để biết thêm thông tin về chủ đề này, hãy tìm kiếm "Multi Render Target" (tàu điện ngầm).


Thông tin tốt đẹp, cảm ơn Julien. Thêm một câu hỏi: Làm thế nào tôi có thể sử dụng độ sâu hoặc thông tin bình thường trong shader mảnh của tôi? Tôi vẫn còn khá mới với GLSL và do đó không biết làm thế nào để đưa các giá trị vào thuật toán Sobel.
enne87

2
Khi bạn tạo bộ đệm khung cho thẻ kết xuất của mình, bạn có thể đính kèm nhiều hơn một kết cấu vào nó. Nếu bạn đính kèm một kết cấu độ sâu, nó sẽ được sử dụng tự động cho độ sâu. Nếu bạn đính kèm một kết cấu màu khác, bạn có thể viết riêng vào đó (xem opengl.org/sdk/docs/man/html/glDrawBuffers.xhtml ), một tính năng có tên là "Multi Render Target" (MRT).
Julien Guertault

@ enne87: Rất vui được giúp đỡ. :)
Julien Guertault

@trichoplax: cảm ơn bạn đã gợi ý; làm xong.
Julien Guertault
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.