Máy dò cạnh Sobel


12

Nhiệm vụ của bạn là viết một chương trình lấy hình ảnh đầu vào và chạy nó thông qua phát hiện cạnh để trở thành hình ảnh đầu ra.

Phát hiện cạnh hoạt động như sau (nếu không rõ ràng, xem phát hiện cạnh sobel ):

  • Giá trị cho một pixel là tổng độ sáng của một pixel, vì vậy nếu nó có màu, trước tiên bạn cần chuyển đổi nó thành thang độ xám (để giữ cho mọi thứ đơn giản và có thể chơi gôn, bạn có thể lấy giá trị trung bình cho R, G và B).
  • Các công thức cho G x và G y cho pixel p (i, j) là:
    • G x = -1 * p (i-1, j-1) - 2 * p (i-1, j) - 1 * p (i-1, j + 1) + 1 * p (i + 1, j -1) + 2 * p (i + 1, j) + 1 * p (i + 1, j + 1)
    • G y = -1 * p (i-1, j-1) - 2 * p (i, j-1) - 1 * p (i + 1, j-1) + 1 * p (i-1, j +1) + 2 * p (i, j + 1) + 1 * p (i + 1, j + 1)
  • Giá trị cho kích thước của cạnh tại pixel đó là: (G x 2 + G y 2 )

Hình ảnh đầu ra là cho mỗi pixel kích thước của cạnh (G x 2 + G y 2 ) dưới dạng thang độ xám.

Tiền thưởng:

  • Thực hiện làm mờ gaussian để làm mịn hình ảnh trước khi phát hiện cạnh, để bỏ qua mọi cạnh nhỏ hơn. Điều này mang lại tiền thưởng -30% cho kết quả cuối cùng.
  • Lấy góc của cạnh trong tài khoản. Bạn cung cấp cho pixel đầu ra một số màu, bằng cách lấy cùng một giá trị thang độ xám và thêm màu từ bánh xe màu bằng góc thu được từ công thức arctan (G y / G x ). Điều này mang lại một phần thưởng khác là -30% cho kết quả cuối cùng.

Quy tắc:

  • Bạn có thể bỏ qua giá trị cho các edgepixels và đặt chúng thành màu đen hoặc bạn có thể sử dụng 0 cho bất kỳ pixel nào bên ngoài hình ảnh.
  • Hình ảnh ouput của bạn phải ở định dạng hình ảnh có thể được mở trên hầu hết các máy tính.
  • Đầu ra phải được ghi vào đĩa hoặc có thể chuyển thành tập tin.
  • Đầu vào được đưa ra dưới dạng đối số dòng lệnh, dưới dạng một đường dẫn tương đối đến hình ảnh hoặc được lấy từ dòng lệnh.
  • Đây là mã golf, vì vậy mã ngắn nhất trong byte thắng!

Bạn có thể chỉ định chính xác độ mờ gaussian? Là thang độ xám đầu vào, nếu không, chúng ta nên áp dụng phát hiện cạnh này cho hình ảnh màu như thế nào? Có đúng không khi hình ảnh đầu ra có cùng kích thước với đầu vào, nhưng đầu vào chỉ được thực hiện trên các pixel bên trong (không phải hình ảnh chúng tôi đặt thành không)?
flawr

Bạn đã xem các video về phát hiện cạnh từ Computerphile chưa? Tôi có thể ngửi thấy một kết nối ở đó :)
GiantTree

@flawr Tôi phải kiểm tra xem gaussian Blur nào tốt cho phát hiện cạnh, vì vậy tôi không thực sự biết đâu là giá trị tốt. thêm về mờ Gaussian ở đây . Hình ảnh đầu vào có màu và trước tiên bạn cần chuyển đổi nó thành thang độ xám nếu bạn muốn thực hiện phát hiện cạnh. Việc phát hiện cạnh được thực hiện bằng A: trên các pixel bên trong và bạn đặt đường viền 1px bên ngoài của hình ảnh đầu ra thành màu đen hoặc B: trên tất cả các pixel và bạn lấy 0 làm giá trị cho bất kỳ pixel nào bên ngoài hình ảnh.
vrwim

@GiantTree nooooooo video hoàn toàn không liên quan :)
vrwim

4
Tại sao điều này đã được bỏ phiếu? Nó dường như là một câu hỏi hoàn toàn hợp lệ.
Addison Crump

Câu trả lời:


13

J, 166 164 161 154 150 144 143 byte.

Không chơi golf quá nhiều; Tôi chủ yếu thu gọn việc thực hiện lâu hơn (xem bên dưới), vì vậy có lẽ có rất nhiều chỗ để cải thiện. Sử dụng thư viện BMP. Lưu kết quả trong tập tin o. Tôi đã xử lý edgepixels bằng cách chỉ sử dụng các ô 3x3 đầy đủ, vì vậy hình ảnh cuối cùng có chiều rộng và chiều cao nhỏ hơn 2 pixel.

load'bmp'
S=:s,.0,.-s=:1 2 1
p=:([:*:[:+/[:,*)"2
'o'writebmp~256#.3#"0<.255<.%:(S&p+(|:S)&p)3 3,.;._3(3%~])+/"1(3#256)#:readbmp}:stdin''
exit''

Sử dụng:

echo 'image.bmp' | jconsole golf.ijs

Mở rộng:

load 'bmp'

sobel1 =: 3 3 $ 1 0 _1 2 0 _2 1 0 _1
NB. transposed
sobel2 =: |: sobel1
NB. read image
image =: readbmp }: stdin''
NB. convert default representation to R,G,B arrays
rgbimage =: (3 # 256) #: image
NB. convert to grayscale
greyimage =: 3 %~ (+/"1) rgbimage
NB. 3x3 cells around each pixel
cells =: 3 3 ,.;._3 greyimage
NB. multiply 3x3 cell by 3x3 sobel, then sum all values in it
partial =: 4 : '+/"1 +/"1 x *"2 y'
NB. square partial (vertical and horizontal) results, sum and root
combine =: [: %: *:@[ + *:@]
NB. limit RGB values to 255
limit =: 255 <. ]
newimage =: limit (sobel1&partial combine sobel2&partial) cells
NB. convert back to J-friendly representation
to_save =: 256 #. 3 #"0 <. newimage
to_save writebmp 'out.bmp'
NB. jconsole stays open by default
exit''

Mẫu đầu vào và đầu ra:

Nguyên Phát hiện cạnh


Đây là một ví dụ hay về ;._3toán tử subarray. Tôi nhận thấy bạn đã xác định một động từ pcó thứ hạng 2 để hoạt động trên các phần con sau khi bạn tạo chúng. Thay vào đó, bạn có thể hoạt động trên mỗi phân đoạn khi bạn cắt. Cố gắng của tôi trong việc thực hiện nó dựa trên công việc của bạn là 256#.3#"0<.255<.3 3((|:S)&*+&.*:&(+/)&,S&*);._3%&3(3#256)+/@#:. Điều đó sẽ đưa nó xuống tổng cộng 126 byte.
dặm

Tôi đã giảm xuống còn 119 byte với 'o'writebmp~256#.3#"0<.255<.3 3(*+&.*:&(+/)&,(*|:))&((-,.0,.])1 2 1);._3%&3(3#256)+/@#:readbmp]stdin''giả sử chỉ có tên tệp là đầu vào trên stdin. Bạn có thể thực hiện điều này bằng cách sử dụng echo -nmột dòng mới bổ sung không được bao gồm trong stdin. Trên máy tính của tôi, tập lệnh tự động thoát khi sử dụng đầu vào có đường dẫn đến tập lệnh, điều đó có nghĩa là tôi không phải đưa vào exit''và có thể lưu thêm 6 byte, nhưng tôi không chắc liệu điều này có đúng với tất cả không.
dặm

1

Python, 161 * 0,7 = 112,7 byte

Với phần thưởng Gaussian Blur.

Vì bạn không rõ ràng cấm các phương thức tích hợp sẵn, đây là OpenCV:

from cv2 import*
from numpy import*
g=GaussianBlur(cvtColor(imread(raw_input()),6),(3,3),sigmaX=1)
x,y=Sobel(g,5,1,0),Sobel(g,5,0,1)
imwrite('s.png',sqrt(x*x+y*y))

Không có tiền thưởng, 136 byte

from cv2 import*
from numpy import*
g=cvtColor(imread(raw_input()),6)
x,y=Sobel(g,5,1,0),Sobel(g,5,0,1)
imwrite('s.png',sqrt(x*x+y*y))
  • Edit1: Thay thế các hằng số được đặt tên bằng các giá trị của chúng.
  • Edit2: Đã tải lên mẫu

nguyên đã lọc


Bạn có thể cho một hình ảnh đầu vào và đầu ra mẫu?
R. Kap

@ R.Kap muộn còn hơn không.
Karl Napf

0

MATLAB, 212 * 0,4 = 84,8 byte

Sử dụng hộp công cụ bộ lọc và không gian màu HSV

function f(x);f=@(i,x)imfilter(i,x);s=@(x)fspecial(x);S=s('sobel');A=f(double(rgb2gray(imread(x)))/255,s('gaussian'));X=f(A,S);Y=f(A,S');imwrite(hsv2rgb(cat(3,atan2(Y,X)/pi/2+0.5,0*A+1,sqrt(X.^2+Y.^2))),'t.png')

hoặc vô lương tâm

function f(x)
f=@(i,x)imfilter(i,x);
s=@(x)fspecial(x);
S=s('sobel');
A=f(double(rgb2gray(imread(x)))/255,s('gaussian'));
X=f(A,S);
Y=f(A,S');
imwrite(hsv2rgb(cat(3,atan2(Y,X)/pi/2+0.5,0*A+1,sqrt(X.^2+Y.^2))),'t.png')

0

Love2D Lua, 466 Byte

A=arg[2]i=love.image.newImageData q=math t=i(A)g=i(t:getWidth()-2,t:getHeight()-2)m={{-1,-2,-1},{0,0,0},{1,2,1}}M={{-1,0,1},{-2,0,2},{-1,0,1}}t:mapPixel(function(_,_,r,g,b)a=(r+g+b)/3 return a,a,a end)g:mapPixel(function(x,y)v=0 for Y=0,2 do for X=0,2 do v=v+(t:getPixel(x+X,y+Y)*m[Y+1][X+1])end end V=0 for Y=0,2 do for X=0,2 do V=V+(t:getPixel(x+X,y+Y)*M[Y+1][X+1])end end v=q.max(q.min(q.sqrt(V^2+v^2),255),0)return v,v,v end)g:encode('png',"o")love.event.quit()

Đưa đầu vào dòng lệnh, xuất ra một tệp có tên "o" trong thư mục ứng dụng Love2D của bạn. Love2D Không cho phép bạn lưu tệp ở bất kỳ nơi nào khác.

Chỉ cần chơi golf như tôi có thể có được nó, có thể có thể được chơi gôn hơn nữa.

Giải thích

-- Assign the Input to A
A=arg[2]


-- Assign some macros to save FUTURE BYTES™
i=love.image.newImageData
q=math

-- t is the original image, g is the new output image. g is two pixels smaller, which is easier and better looking than a border.
t = i(A)
g = i(t:getWidth()-2,t:getHeight()-2)

-- m and M are our two sobel kernals. Fairly self explanitary.
m = {{-1,-2,-1}
    ,{0,0,0}
    ,{1,2,1}}

M = {{-1,0,1}
    ,{-2,0,2}
    ,{-1,0,1}}

-- Convert t to grayscale, to save doing this math later.
t:mapPixel(function(_,_,r,g,b)a=(r+g+b)/3 return a,a,a end)

-- Execute our kernals
g:mapPixel(function(x,y)
    -- v refers to the VERTICAL output of the Kernel m.
    v=0
    for Y=0,2 do
        for X=0,2 do
            v=v+(t:getPixel(x+X,y+Y)*m[Y+1][X+1])
        end
    end

    -- V is the HORIZONTAL of M
    V=0
    for Y=0,2 do
        for X=0,2 do
            V=V+(t:getPixel(x+X,y+Y)*M[Y+1][X+1])
        end
    end

    -- Clamp the values and sum them.
    v = q.max(q.min(q.sqrt(V^2 + v^2),255),0)
    -- Return the grayscale.
    return v,v,v
end)

-- Save, renaming the file. The golfed version just outputs as 'o'
g:encode('png',"S_".. A:gsub("(.*)%....","%1.png"))

-- Quit. Not needed, but I'm a sucker for self contained LOVE2D
love.event.quit()

Kiểm tra

Đầu vào Đầu ra

Và ...

Mặc dù nó không thực sự cải thiện điểm số của tôi (Làm cho nó tệ hơn nguyên vẹn), đây là phiên bản với bánh xe màu được triển khai.

900 - 270 = 630 byte

A=arg[2]i=love.image.newImageData q=math t=i(A)g=i(t:getWidth()-2,t:getHeight()-2)m={{-1,-2,-1},{0,0,0},{1,2,1}}M={{-1,0,1},{-2,0,2},{-1,0,1}}function T(h,s,v)if s <=0 then return v,v,v end h,s,v=h*6,s,v/255 local c=v*s local x=(1-q.abs((h%2)-1))*c local m,r,g,b=(v-c),0,0,0 if h < 1 then r,g,b=c,x,0 elseif h < 2 then r,g,b=x,c,0 elseif h < 3 then r,g,b=0,c,x elseif h < 4 then r,g,b=0,x,c elseif h < 5 then r,g,b=x,0,c else r,g,b=c,0,x end return(r+m)*255,(g+m)*255,(b+m)*255 end t:mapPixel(function(_,_,r,g,b)a=(r+g+b)/3 return a,a,a end)g:mapPixel(function(x,y)v=0 for Y=0,2 do for X=0,2 do v=v+(t:getPixel(x+X,y+Y)*m[Y+1][X+1])end end V=0 for Y=0,2 do for X=0,2 do V=V+(t:getPixel(x+X,y+Y)*M[Y+1][X+1])end end h=v H=V v=q.max(q.min(q.sqrt(V^2+v^2),255),0)h=q.atan2(H,h)/q.pi*2 return T(h,1,v,255)end)g:encode('png',"S_".. A:gsub("(.*)%....","%1.png"))G=love.graphics.newImage(g)love.event.quit()

nhập mô tả hình ảnh ở đây

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.