Tự động phát hiện góc quay trên hình ảnh tùy ý với các tính năng trực giao


9

Tôi có một nhiệm vụ trong tay khi tôi cần phát hiện góc của một hình ảnh như mẫu sau (một phần của ảnh vi mạch). Hình ảnh không chứa các đặc điểm trực giao, nhưng chúng có thể có kích thước khác nhau, với độ phân giải / độ sắc nét khác nhau. Hình ảnh sẽ hơi không hoàn hảo do một số biến dạng quang học và quang sai. Độ chính xác phát hiện góc pixel phụ là bắt buộc (nghĩa là nó phải ở dưới sai số <0,1 °, một cái gì đó như 0,01 ° sẽ được chấp nhận). Để tham khảo, đối với hình ảnh này góc tối ưu là khoảng 32,19 °.

nhập mô tả hình ảnh ở đây Hiện tại tôi đã thử 2 cách tiếp cận: Cả hai thực hiện tìm kiếm vũ phu cho mức tối thiểu cục bộ với bước 2 °, sau đó độ dốc giảm xuống kích thước bước 0,0001 °.

  1. Chức năng được sum(pow(img(x+1)-img(x-1), 2) + pow(img(y+1)-img(y-1))tính trên hình ảnh. Khi các đường ngang / dọc được căn chỉnh - sẽ có ít thay đổi theo hướng ngang / dọc. Độ chính xác là khoảng 0,2 °.
  2. Chức năng là (tối đa) trên một số chiều rộng / chiều cao của hình ảnh. Dải này cũng được lặp trên hình ảnh và chức năng công đức được tích lũy. Cách tiếp cận này cũng tập trung vào sự thay đổi độ sáng nhỏ hơn khi các đường ngang / dọc được căn chỉnh, nhưng nó có thể phát hiện những thay đổi nhỏ hơn trên cơ sở lớn hơn (chiều rộng sọc - có thể rộng khoảng 100 pixel). Điều này cho độ chính xác tốt hơn, lên tới 0,01 ° - nhưng có rất nhiều thông số cần điều chỉnh (ví dụ chiều rộng / chiều cao sọc khá nhạy) có thể không đáng tin cậy trong thế giới thực.

Bộ lọc phát hiện cạnh không giúp được gì nhiều.

Mối quan tâm của tôi là thay đổi rất nhỏ trong chức năng công đức trong cả hai trường hợp giữa góc xấu nhất và tốt nhất (chênh lệch <2 lần).

Bạn có bất cứ đề nghị tốt hơn về viết chức năng công đức để phát hiện góc?

Cập nhật: Hình ảnh mẫu kích thước đầy đủ được tải lên tại đây (51 MiB)

Sau khi xử lý xong, nó sẽ trông như thế này.


1
Thật đáng buồn khi nó đã được chuyển từ stackoverflow sang dsp. Tôi không thấy một giải pháp giống như DSP ở đây và cơ hội bây giờ đã giảm đi nhiều. 99,9% thuật toán và thủ thuật DSP là vô ích cho nhiệm vụ này. Có vẻ như thuật toán tùy chỉnh hoặc cách tiếp cận là cần thiết ở đây, không phải là FFT.
BarsMonster

2
Tôi rất vui khi nói với bạn rằng buồn là hoàn toàn sai; DSP.SE là nơi tuyệt đối đúng để hỏi điều này! (không quá nhiều stackoverflow. Đây không phải là một câu hỏi lập trình. Bạn biết lập trình của mình. Bạn không biết cách xử lý hình ảnh này.) Hình ảnh là tín hiệu và DSP.SE rất quan tâm đến việc xử lý hình ảnh! Ngoài ra, rất nhiều thủ thuật DSP chung (thậm chí được biết đến như các tín hiệu liên lạc) rất có thể áp dụng cho vấn đề của bạn :)
Marcus Müller

1
Hiệu quả quan trọng như thế nào?
Cedron Dawg

Nhân tiện, ngay cả khi chạy với độ phân giải 0,04 °, tôi khá chắc chắn góc xoay chính xác là 32 ° chứ không phải 32,19 ° - độ phân giải của ảnh gốc của bạn là gì? Bởi vì ở chiều rộng 800 px, một vòng quay không đáng lo ngại là 0,01 ° nhưng chênh lệch chiều cao 0,14 px, và điều đó thậm chí sẽ không được chú ý.
Marcus Müller

@CedronDawg Chắc chắn không có yêu cầu thời gian thực, tôi có thể chịu đựng được khoảng 10-60 giây tính toán trên một số lõi 8-12.
BarsMonster

Câu trả lời:


12

Nếu tôi hiểu chính xác phương pháp 1 của bạn, với nó, nếu bạn đã sử dụng một vùng đối xứng tròn và thực hiện xoay quanh tâm của khu vực, bạn sẽ loại bỏ sự phụ thuộc của khu vực vào góc xoay và có được sự so sánh công bằng hơn giữa chức năng công đức giữa góc quay khác nhau. Tôi sẽ đề xuất một phương pháp về cơ bản tương đương với phương pháp đó, nhưng sử dụng hình ảnh đầy đủ và không yêu cầu xoay hình ảnh lặp lại, và sẽ bao gồm bộ lọc thông thấp để loại bỏ bất đẳng hướng lưới pixel và để khử nhiễu.

Gradient của hình ảnh được lọc đẳng hướng thấp

Trước tiên, hãy tính toán một vectơ độ dốc cục bộ tại mỗi pixel cho kênh màu xanh lục trong hình ảnh mẫu kích thước đầy đủ.

Tôi đã tạo ra các hạt nhân phân biệt ngang và dọc bằng cách phân biệt đáp ứng xung không gian liên tục của bộ lọc thông thấp lý tưởng với đáp ứng tần số tròn phẳng loại bỏ hiệu ứng của sự lựa chọn trục hình ảnh bằng cách đảm bảo rằng không có mức độ chi tiết khác nhau được so sánh theo đường chéo theo chiều ngang hoặc chiều dọc, bằng cách lấy mẫu hàm kết quả và bằng cách áp dụng cửa sổ cosine xoay:

(1)hx[x,y]={0if x=y=0,ωc2xJ2(ωcx2+y2)2π(x2+y2)otherwise,hy[x,y]={0if x=y=0,ωc2yJ2(ωcx2+y2)2π(x2+y2)otherwise,

Ở đâu J2 là một hàm Bessel thứ 2 thuộc loại thứ nhất, và ωclà tần số cắt theo radian. Nguồn Python (không có dấu trừ của phương trình 1):

import matplotlib.pyplot as plt
import scipy
import scipy.special
import numpy as np

def rotatedCosineWindow(N):  # N = horizontal size of the targeted kernel, also its vertical size, must be odd.
  return np.fromfunction(lambda y, x: np.maximum(np.cos(np.pi/2*np.sqrt(((x - (N - 1)/2)/((N - 1)/2 + 1))**2 + ((y - (N - 1)/2)/((N - 1)/2 + 1))**2)), 0), [N, N])

def circularLowpassKernelX(omega_c, N):  # omega = cutoff frequency in radians (pi is max), N = horizontal size of the kernel, also its vertical size, must be odd.
  kernel = np.fromfunction(lambda y, x: omega_c**2*(x - (N - 1)/2)*scipy.special.jv(2, omega_c*np.sqrt((x - (N - 1)/2)**2 + (y - (N - 1)/2)**2))/(2*np.pi*((x - (N - 1)/2)**2 + (y - (N - 1)/2)**2)), [N, N])
  kernel[(N - 1)//2, (N - 1)//2] = 0
  return kernel

def circularLowpassKernelY(omega_c, N):  # omega = cutoff frequency in radians (pi is max), N = horizontal size of the kernel, also its vertical size, must be odd.
  kernel = np.fromfunction(lambda y, x: omega_c**2*(y - (N - 1)/2)*scipy.special.jv(2, omega_c*np.sqrt((x - (N - 1)/2)**2 + (y - (N - 1)/2)**2))/(2*np.pi*((x - (N - 1)/2)**2 + (y - (N - 1)/2)**2)), [N, N])
  kernel[(N - 1)//2, (N - 1)//2] = 0
  return kernel

N = 41  # Horizontal size of the kernel, also its vertical size. Must be odd.
window = rotatedCosineWindow(N)

# Optional window function plot
#plt.imshow(window, vmin=-np.max(window), vmax=np.max(window), cmap='bwr')
#plt.colorbar()
#plt.show()

omega_c = np.pi/4  # Cutoff frequency in radians <= pi
kernelX = circularLowpassKernelX(omega_c, N)*window
kernelY = circularLowpassKernelY(omega_c, N)*window

# Optional kernel plot
#plt.imshow(kernelX, vmin=-np.max(kernelX), vmax=np.max(kernelX), cmap='bwr')
#plt.colorbar()
#plt.show()

nhập mô tả hình ảnh ở đây
Hình 1. Cửa sổ cosin xoay 2 chiều.

nhập mô tả hình ảnh ở đây
nhập mô tả hình ảnh ở đây
nhập mô tả hình ảnh ở đây
Hình 2. Các hạt nhân phân biệt đẳng hướng thấp ngang cửa sổ, cho các tần số cắt khác nhau ωccài đặt. Trên cùng: omega_c = np.pigiữa : omega_c = np.pi/4, dưới : omega_c = np.pi/16. Dấu trừ của phương trình. 1 đã bị bỏ lại. Hạt nhân dọc trông giống nhau nhưng đã được xoay 90 độ. Tổng trọng số của hạt nhân ngang và dọc, với trọng sốcos(ϕ)sin(ϕ), tương ứng, đưa ra một hạt nhân phân tích cùng loại cho góc dốc ϕ.

Sự khác biệt của đáp ứng xung không ảnh hưởng đến băng thông, như có thể thấy bằng biến đổi Fourier nhanh 2-d (FFT) của nó, trong Python:

# Optional FFT plot
absF = np.abs(np.fft.fftshift(np.fft.fft2(circularLowpassKernelX(np.pi, N)*window)))
plt.imshow(absF, vmin=0, vmax=np.max(absF), cmap='Greys', extent=[-np.pi, np.pi, -np.pi, np.pi])
plt.colorbar()
plt.show()

nhập mô tả hình ảnh ở đây
Hình 3. Tầm quan trọng của FFT 2-d của hx. Trong miền tần số, sự khác biệt xuất hiện dưới dạng phép nhân của dải thông tròn phẳng bằngωxvà bởi sự dịch chuyển pha 90 độ không thể nhìn thấy ở cường độ.

Để thực hiện tích chập cho kênh màu xanh lá cây và thu thập biểu đồ vectơ độ dốc 2 chiều, để kiểm tra trực quan, bằng Python:

import scipy.ndimage

img = plt.imread('sample.tif').astype(float)
X = scipy.ndimage.convolve(img[:,:,1], kernelX)[(N - 1)//2:-(N - 1)//2, (N - 1)//2:-(N - 1)//2]  # Green channel only
Y = scipy.ndimage.convolve(img[:,:,1], kernelY)[(N - 1)//2:-(N - 1)//2, (N - 1)//2:-(N - 1)//2]  # ...

# Optional 2-d histogram
#hist2d, xEdges, yEdges = np.histogram2d(X.flatten(), Y.flatten(), bins=199)
#plt.imshow(hist2d**(1/2.2), vmin=0, cmap='Greys')
#plt.show()
#plt.imsave('hist2d.png', plt.cm.Greys(plt.Normalize(vmin=0, vmax=hist2d.max()**(1/2.2))(hist2d**(1/2.2))))  # To save the histogram image
#plt.imsave('histkey.png', plt.cm.Greys(np.repeat([(np.arange(200)/199)**(1/2.2)], 16, 0)))

Điều này cũng cắt dữ liệu, loại bỏ các (N - 1)//2pixel từ mỗi cạnh bị ô nhiễm bởi ranh giới hình ảnh hình chữ nhật, trước khi phân tích biểu đồ.

nhập mô tả hình ảnh ở đâyπ nhập mô tả hình ảnh ở đâyπ2 nhập mô tả hình ảnh ở đâyπ4
nhập mô tả hình ảnh ở đâyπ8 nhập mô tả hình ảnh ở đâyπ16 nhập mô tả hình ảnh ở đâyπ32 nhập mô tả hình ảnh ở đâyπ64 nhập mô tả hình ảnh ở đây-0
Hình 4. Biểu đồ 2 chiều của vectơ độ dốc, cho tần số cắt bộ lọc thông thấp khác nhau ωccài đặt. Theo thứ tự: đầu tiên với N=41: omega_c = np.pi, omega_c = np.pi/2, omega_c = np.pi/4(giống như trong Python niêm yết), omega_c = np.pi/8, omega_c = np.pi/16, sau đó: N=81: omega_c = np.pi/32, N=161: omega_c = np.pi/64. Khử nhiễu bằng cách lọc thông thấp làm sắc nét các hướng gradient theo dõi mạch trong biểu đồ.

Vector chiều dài trọng số tròn có nghĩa là hướng

phương pháp Yamartino để tìm hướng gió "trung bình" từ nhiều mẫu vectơ gió trong một lần đi qua các mẫu. Nó dựa trên giá trị trung bình của các đại lượng tròn , được tính là sự dịch chuyển của một cosin là tổng của các cosin được dịch chuyển bởi một lượng tròn2π. Chúng ta có thể sử dụng một phiên bản có trọng số chiều dài vectơ của cùng một phương thức, nhưng trước tiên chúng ta cần tập hợp tất cả các hướng bằng nhau với moduloπ/2. Chúng ta có thể làm điều này bằng cách nhân góc của mỗi vectơ gradient[Xk,Yk] bằng 4, sử dụng biểu diễn số phức:

(2)Zk=(Xk+Yki)4Xk2+Yk23=Xk46Xk2Yk2+Yk4+(4Xk3Yk-4XkYk3)TôiXk2+Yk23,

thỏa mãn |Zk|= =Xk2+Yk2 và sau đó giải thích rằng các giai đoạn của Zk từ -π đến π đại diện cho các góc từ -π/4 đến π/4, bằng cách chia pha trung bình tính toán cho 4:

(3)φ= =14atan2(ΣkTôi đang(Zk),ΣkTái(Zk))

Ở đâu φ là định hướng hình ảnh ước tính.

Chất lượng của ước tính có thể được đánh giá bằng cách thực hiện một lần nữa thông qua dữ liệu và bằng cách tính khoảng cách hình tròn vuông có trọng số trung bình ,MSCD, giữa các pha của số phức Zk và pha trung bình tròn ước tính 4φ, với |Zk| như trọng lượng:

(4)MSCD= =Σk|Zk|(1-cos(4φ-atan2(Tôi đang(Zk),Tái(Zk))))Σk|Zk|= =Σk|Zk|2((cos(4φ)-Tái(Zk)|Zk|)2+(tội(4φ)-Tôi đang(Zk)|Zk|)2)Σk|Zk|= =Σk(|Zk|-Tái(Zk)cos(4φ)-Tôi đang(Zk)tội(4φ))Σk|Zk|,

được giảm thiểu bởi φtính trên mỗi phương trình. 3. Trong Python:

absZ = np.sqrt(X**2 + Y**2)
reZ = (X**4 - 6*X**2*Y**2 + Y**4)/absZ**3
imZ = (4*X**3*Y - 4*X*Y**3)/absZ**3
phi = np.arctan2(np.sum(imZ), np.sum(reZ))/4

sumWeighted = np.sum(absZ - reZ*np.cos(4*phi) - imZ*np.sin(4*phi))
sumAbsZ = np.sum(absZ)
mscd = sumWeighted/sumAbsZ

print("rotate", -phi*180/np.pi, "deg, RMSCD =", np.arccos(1 - mscd)/4*180/np.pi, "deg equivalent (weight = length)")

Dựa trên các mpmaththí nghiệm của tôi (không được hiển thị), tôi nghĩ rằng chúng ta sẽ không hết tiền số ngay cả đối với các hình ảnh rất lớn. Đối với các cài đặt bộ lọc khác nhau (được chú thích), các đầu ra được báo cáo trong khoảng từ -45 đến 45 độ:

rotate 32.29809399495655 deg, RMSCD = 17.057059965741338 deg equivalent (omega_c = np.pi)
rotate 32.07672617150525 deg, RMSCD = 16.699056648843566 deg equivalent (omega_c = np.pi/2)
rotate 32.13115293914797 deg, RMSCD = 15.217534399922902 deg equivalent (omega_c = np.pi/4, same as in the Python listing)
rotate 32.18444156018288 deg, RMSCD = 14.239347706786056 deg equivalent (omega_c = np.pi/8)
rotate 32.23705383489169 deg, RMSCD = 13.63694582160468 deg equivalent (omega_c = np.pi/16)

Lọc thông thấp mạnh có vẻ hữu ích, giảm góc tương đương với khoảng cách vuông góc trung bình (RMSCD) được tính như acos(1-MSCD). Nếu không có cửa sổ cosin xoay 2 ngày, một số kết quả sẽ bị tắt theo một mức độ hoặc không (không hiển thị), điều đó có nghĩa là điều quan trọng là phải thực hiện đúng cửa sổ của các bộ lọc phân tích. Góc tương đương RMSCD không trực tiếp là ước tính sai số trong ước tính góc, nên ít hơn nhiều.

Hàm trọng lượng thay thế chiều dài hình vuông

Hãy thử bình phương chiều dài vectơ như một hàm trọng số thay thế, bằng cách:

(5)Zk= =(Xk+YkTôi)4Xk2+Yk22= =Xk4-6Xk2Yk2+Yk4+(4Xk3Yk-4XkYk3)TôiXk2+Yk2,

Trong Python:

absZ_alt = X**2 + Y**2
reZ_alt = (X**4 - 6*X**2*Y**2 + Y**4)/absZ_alt
imZ_alt = (4*X**3*Y - 4*X*Y**3)/absZ_alt
phi_alt = np.arctan2(np.sum(imZ_alt), np.sum(reZ_alt))/4

sumWeighted_alt = np.sum(absZ_alt - reZ_alt*np.cos(4*phi_alt) - imZ_alt*np.sin(4*phi_alt))
sumAbsZ_alt = np.sum(absZ_alt)
mscd_alt = sumWeighted_alt/sumAbsZ_alt

print("rotate", -phi_alt*180/np.pi, "deg, RMSCD =", np.arccos(1 - mscd_alt)/4*180/np.pi, "deg equivalent (weight = length^2)")

Trọng lượng chiều dài hình vuông làm giảm góc tương đương RMSCD khoảng một độ:

rotate 32.264713568426764 deg, RMSCD = 16.06582418749094 deg equivalent (weight = length^2, omega_c = np.pi, N = 41)
rotate 32.03693157762725 deg, RMSCD = 15.839593856962486 deg equivalent (weight = length^2, omega_c = np.pi/2, N = 41)
rotate 32.11471435914187 deg, RMSCD = 14.315371970649874 deg equivalent (weight = length^2, omega_c = np.pi/4, N = 41)
rotate 32.16968341455537 deg, RMSCD = 13.624896827482049 deg equivalent (weight = length^2, omega_c = np.pi/8, N = 41)
rotate 32.22062839958777 deg, RMSCD = 12.495324176281466 deg equivalent (weight = length^2, omega_c = np.pi/16, N = 41)
rotate 32.22385477783647 deg, RMSCD = 13.629915935941973 deg equivalent (weight = length^2, omega_c = np.pi/32, N = 81)
rotate 32.284350817263906 deg, RMSCD = 12.308297934977746 deg equivalent (weight = length^2, omega_c = np.pi/64, N = 161)

Đây có vẻ là một chức năng trọng lượng tốt hơn một chút. Tôi cũng thêm vàoωc= =π/32ωc= =π/64. Họ sử dụng lớn hơn Ndẫn đến việc cắt xén hình ảnh khác nhau và không so sánh nghiêm ngặt các giá trị MSCD.

Biểu đồ 1-d

Lợi ích của hàm trọng lượng chiều dài hình vuông rõ ràng hơn với biểu đồ trọng số 1-d của Zkcác giai đoạn. Tập lệnh Python:

# Optional histogram
hist_plain, bin_edges = np.histogram(np.arctan2(imZ, reZ), weights=np.ones(absZ.shape)/absZ.size, bins=900)
hist, bin_edges = np.histogram(np.arctan2(imZ, reZ), weights=absZ/np.sum(absZ), bins=900)
hist_alt, bin_edges = np.histogram(np.arctan2(imZ_alt, reZ_alt), weights=absZ_alt/np.sum(absZ_alt), bins=900)
plt.plot((bin_edges[:-1]+(bin_edges[1]-bin_edges[0]))*45/np.pi, hist_plain, "black")
plt.plot((bin_edges[:-1]+(bin_edges[1]-bin_edges[0]))*45/np.pi, hist, "red")
plt.plot((bin_edges[:-1]+(bin_edges[1]-bin_edges[0]))*45/np.pi, hist_alt, "blue")
plt.xlabel("angle (degrees)")
plt.show()

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây
Hình 5. Biểu đồ trọng số được nội suy tuyến tính của các góc vectơ gradient, được bao bọc -π/4Giáo dụcπ/4và có trọng số theo (theo thứ tự từ dưới lên trên ở đỉnh): không có trọng số (màu đen), chiều dài vectơ gradient (màu đỏ), hình vuông có độ dài vectơ độ dốc (màu xanh). Chiều rộng thùng là 0,1 độ. Bộ lọc bị cắt omega_c = np.pi/4, giống như trong danh sách Python. Hình dưới được phóng to ở các đỉnh.

Toán lọc ổn định

Chúng tôi đã thấy rằng phương pháp này hoạt động, nhưng sẽ tốt hơn nếu có sự hiểu biết toán học tốt hơn. Cácxyđáp ứng bộ lọc phân biệt được đưa ra bởi biểu thức. 1 có thể được hiểu là các hàm cơ bản để hình thành đáp ứng xung của bộ lọc phân biệt có thể điều khiển được lấy mẫu từ một vòng quay bên phải của phương trình chohx[x,y](Phương trình 1). Điều này dễ thấy hơn bằng cách chuyển đổi phương trình. Tọa độ 1 đến cực:

(6)hx(r,θ)= =hx[rcos(θ),rtội(θ)]= ={0nếu r= =0,-ωc2rcos(θ)J2(ωcr)2πr2nếu không thì= =cos(θ)f(r),hy(r,θ)= =hy[rcos(θ),rtội(θ)]= ={0nếu r= =0,-ωc2rtội(θ)J2(ωcr)2πr2nếu không thì= =tội(θ)f(r),f(r)= ={0nếu r= =0,-ωc2rJ2(ωcr)2πr2nếu không thì,

trong đó cả đáp ứng xung của bộ lọc phân biệt ngang và dọc có cùng chức năng nhân tố xuyên tâm f(r). Bất kỳ phiên bản xoayh(r,θ,φ) của hx(r,θ) bằng góc lái φ được lấy bởi:

(7)h(r,θ,φ)= =hx(r,θ-φ)= =cos(θ-φ)f(r)

Ý tưởng là hạt nhân lái h(r,θ,φ) có thể được xây dựng như một tổng trọng số của hx(r,θ)hx(r,θ), với cos(φ)tội(φ) như trọng lượng, và đó thực sự là trường hợp:

(số 8)cos(φ)hx(r,θ)+tội(φ)hy(r,θ)= =cos(φ)cos(θ)f(r)+tội(φ)tội(θ)f(r)= =cos(θ-φ)f(r)= =h(r,θ,φ).

Chúng ta sẽ đi đến một kết luận tương đương nếu chúng ta nghĩ về tín hiệu được lọc thông thấp đẳng hướng là tín hiệu đầu vào và xây dựng một toán tử đạo hàm riêng đối với tọa độ đầu tiên được quay xφ, yφ xoay theo góc φ từ tọa độ x, y. (Đạo hàm có thể được coi là một hệ thống bất biến thời gian tuyến tính.) Chúng ta có:

(9)x= =cos(φ)xφ-tội(φ)yφ,y= =tội(φ)xφ+cos(φ)yφ

Sử dụng quy tắc chuỗi cho các đạo hàm riêng, toán tử đạo hàm riêng đối vớixφ có thể được biểu thị dưới dạng tổng trọng số cosin và sin của các đạo hàm riêng đối với xy:

(10)xφ= =xxφx+yxφy= =(cos(φ)xφ-tội(φ)yφ)xφx+(tội(φ)xφ+cos(φ)yφ)xφy= =cos(φ)x+tội(φ)y

Một câu hỏi vẫn còn được khám phá là làm thế nào một giá trị trung bình hình tròn có trọng số phù hợp của các góc vectơ có liên quan đến góc φ về mặt nào đó, bộ lọc phân biệt định hướng "được kích hoạt nhiều nhất".

Cải tiến có thể

Để có thể cải thiện kết quả hơn nữa, độ dốc cũng có thể được tính cho các kênh màu đỏ và xanh lam, được đưa vào dưới dạng dữ liệu bổ sung trong tính toán "trung bình".

Tôi có ý tưởng mở rộng có thể của phương pháp này:

1) Sử dụng một bộ hạt nhân phân tích phân tích lớn hơn và phát hiện các cạnh thay vì phát hiện độ dốc. Điều này cần phải được chế tạo cẩn thận để các cạnh theo mọi hướng được xử lý như nhau, nghĩa là, một máy dò cạnh cho bất kỳ góc nào cũng có thể đạt được bằng một tổng số hạt nhân trực giao. Một tập hợp các hạt nhân phù hợp có thể (tôi nghĩ) có thể thu được bằng cách áp dụng các toán tử vi phân của phương trình. 11, Hình 6 (xem thêm bài Trao đổi ngăn xếp toán học của tôi ) về đáp ứng xung không gian liên tục của bộ lọc thông thấp đối xứng tròn.

(11)limh0ΣN= =04N+1(-1)nf(x+hcos(2πn4N+2),y+htội(2πn4N+2))h2N+1,limh0ΣN= =04N+1(-1)nf(x+htội(2πn4N+2),y+hcos(2πn4N+2))h2N+1

nhập mô tả hình ảnh ở đây
Hình 6. Dirac delta vị trí tương đối trong các toán tử vi sai để xây dựng các máy dò cạnh bậc cao hơn.

2) Việc tính giá trị trung bình (có trọng số) của đại lượng tròn có thể được hiểu là tổng các cosin có cùng tần số được dịch chuyển bởi các mẫu của đại lượng (và được chia theo trọng số) và tìm đỉnh của hàm kết quả. Nếu các sóng hài được dịch chuyển và chia tỷ lệ tương tự của cosin bị dịch chuyển, với biên độ tương đối được chọn cẩn thận, được thêm vào hỗn hợp, tạo thành một hạt làm mịn sắc nét hơn, thì có thể xuất hiện nhiều đỉnh trong tổng và đỉnh có giá trị lớn nhất. Với một hỗn hợp hài hòa phù hợp, điều đó sẽ mang lại một loại trung bình cục bộ mà phần lớn bỏ qua các ngoại lệ ra khỏi đỉnh chính của phân phối.

Cách tiếp cận khác

Cũng có thể kết hợp hình ảnh theo góc φ và góc φ+π/2xoay các hạt nhân "cạnh dài" và để tính bình phương trung bình của các pixel của hai hình ảnh được chập. Gócφmà tối đa hóa bình phương trung bình sẽ được báo cáo. Cách tiếp cận này có thể đưa ra một sàng lọc cuối cùng tốt cho việc tìm hướng định hướng hình ảnh, bởi vì thật nguy hiểm khi tìm kiếm góc hoàn chỉnhφ không gian ở các bước lớn.

Một cách tiếp cận khác là các phương pháp không cục bộ, như các vùng tương tự xa tương quan chéo, có thể áp dụng nếu bạn biết rằng có các dấu vết ngang hoặc dọc dài hoặc các tính năng lặp lại nhiều lần theo chiều ngang hoặc chiều dọc.


Làm thế nào chính xác kết quả bạn nhận được?
Royi

@Royi Có lẽ khoảng 0,1 độ.
Olli Niemitalo

@OlliNiemitalo khá ấn tượng, với độ phân giải hạn chế!
Marcus Müller

3
@OlliNiemitalo nói về ấn tượng: này. câu trả lời. Là. cái đó. từ ngữ. rất. Định nghĩa.
Marcus Müller

@ MarcusMüller Cảm ơn Marcus, tôi dự đoán phần mở rộng đầu tiên cũng rất thú vị.
Olli Niemitalo

5

Có một mẹo DSP tương tự ở đây, nhưng tôi không nhớ chính xác các chi tiết.

Tôi đọc về nó ở đâu đó, một thời gian trước đây. Nó phải làm với việc tìm ra các mẫu vải phù hợp bất kể định hướng. Vì vậy, bạn có thể muốn nghiên cứu về điều đó.

Lấy một mẫu vòng tròn. Làm các khoản tiền dọc theo nan hoa của vòng tròn để có được một hồ sơ chu vi. Sau đó, họ đã thực hiện một DFT về điều đó (rốt cuộc nó đã được thông tư). Xé thông tin pha (làm cho nó định hướng độc lập) và so sánh.

Sau đó, họ có thể biết liệu hai loại vải có cùng một mẫu.

Vấn đề của bạn là tương tự.

Dường như với tôi, mà không cần thử trước, các đặc điểm của hồ sơ DFT trước sẽ tiết lộ định hướng. Làm độ lệch chuẩn dọc theo nan hoa thay vì tổng sẽ hoạt động tốt hơn, có thể cả hai.

Bây giờ, nếu bạn có một hình ảnh tham chiếu định hướng, bạn có thể sử dụng kỹ thuật của họ.

Ced


Yêu cầu chính xác của bạn là khá nghiêm ngặt.

Tôi đã cho điều này một whack. Lấy tổng các giá trị tuyệt đối của sự khác biệt giữa hai điểm tiếp theo dọc theo cách nói cho mỗi màu.

Dưới đây là một biểu đồ xung quanh chu vi. Giá trị của bạn được vẽ với các điểm đánh dấu màu trắng.

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

Bạn có thể thấy nó, nhưng tôi không nghĩ rằng nó sẽ hiệu quả với bạn. Lấy làm tiếc.


Báo cáo tiến độ: Một số

Tôi đã quyết định về một quá trình ba bước.

1) Tìm điểm đánh giá.

2) Đo lường thô

3) Đo lường tốt

Hiện tại, bước đầu tiên là sự chú ý của người dùng. Nó sẽ tự động tương thích, nhưng tôi không bận tâm. Tôi có một bản thảo thô của bước thứ hai. Có một số điều chỉnh tôi muốn thử. Cuối cùng, tôi có một vài ứng cử viên cho bước thứ ba sẽ thực hiện thử nghiệm để xem cái nào hoạt động tốt nhất.

Tin tốt là nó được chiếu sáng nhanh. Nếu mục đích duy nhất của bạn là làm cho mức độ hình ảnh trông trên một trang web, thì dung sai của bạn quá nghiêm ngặt và phép đo thô phải đủ chính xác.

Đây là phép đo thô. Mỗi pixel khoảng 0,6 độ. (Chỉnh sửa, thực sự là 0,3)

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


Báo cáo tiến độ: Có thể nhận được kết quả tốt

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

Hầu hết không tốt như vậy, nhưng chúng rẻ (và khá địa phương) và việc tìm kiếm các điểm để có được đọc tốt là dễ dàng ..... đối với một con người. Lực lượng vũ phu nên làm việc tốt cho một chương trình.

Các kết quả có thể được cải thiện nhiều, đây là một thử nghiệm cơ bản đơn giản. Tôi chưa sẵn sàng để giải thích, cũng không đăng mã, nhưng màn hình này không được photoshop.


Báo cáo tiến độ: Mã được đăng, tôi đã hoàn thành việc này trong một thời gian.

Ảnh chụp màn hình này là chương trình làm việc trên ảnh chụp 45 độ của Marcus.

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

Các kênh màu được xử lý độc lập.

Một điểm được chọn là trung tâm quét.

Một đường kính được quét qua 180 độ ở các góc riêng biệt

Ở mỗi góc, "độ biến động" được đo trên đường kính. Một dấu vết được thực hiện cho mỗi kênh thu thập mẫu. Giá trị mẫu là một phép nội suy tuyến tính của bốn giá trị góc của bất kỳ ô vuông nào mà điểm mẫu nằm trên.

Đối với mỗi dấu vết kênh

Các mẫu được nhân với chức năng cửa sổ VonHann

Một đường chuyền Smooth / Dif được thực hiện trên các mẫu

RMS của sự khác biệt được sử dụng như một biện pháp biến động

Các biểu đồ hàng thấp hơn là:

Đầu tiên là quét từ 0 đến 180 độ, mỗi pixel là 0,5 độ. Thứ hai là quét xung quanh góc đã chọn, mỗi pixel là 0,1 độ. Thứ ba là quét xung quanh góc đã chọn, mỗi pixel là 0,01 độ. Thứ tư là đường cong vi sai

Lựa chọn ban đầu là độ biến động trung bình tối thiểu của ba kênh. Điều này sẽ gần, nhưng thường không phải trên, góc tốt nhất. Sự đối xứng tại máng là một chỉ số tốt hơn mức tối thiểu. Một parabola phù hợp nhất trong khu phố đó sẽ mang lại một câu trả lời rất tốt.

Mã nguồn (trong Gambas, PPA gambas-team / gambas3) có thể được tìm thấy tại:

https://forum.gambas.one/viewtopic.php?f=4&t=707

Đây là một tệp zip thông thường, vì vậy bạn không cần phải cài đặt Gambas để xem nguồn. Các tệp nằm trong thư mục con ".src".

Loại bỏ cửa sổ VonHann mang lại độ chính xác cao hơn vì nó có hiệu quả kéo dài dấu vết, nhưng thêm sự chao đảo. Có lẽ một VonHann đôi sẽ tốt hơn vì trung tâm là không quan trọng và khởi phát nhanh hơn "khi teeter-totter chạm đất" sẽ được phát hiện. Độ chính xác có thể dễ dàng được cải thiện khi tôi tăng chiều dài theo dõi khi hình ảnh cho phép (Có, đó là tự động tương thích). Một chức năng cửa sổ tốt hơn, chân thành?

Các biện pháp tôi đã thực hiện ở cài đặt hiện tại xác nhận giá trị 3.19 +/-. 03 ish.

Đây chỉ là công cụ đo lường. Có một số chiến lược tôi có thể nghĩ ra để áp dụng nó vào hình ảnh. Điều đó, như họ nói, là một bài tập cho người đọc. Hoặc trong trường hợp này, OP. Tôi sẽ cố gắng sau này.

Có phòng đầu để cải tiến cả thuật toán và chương trình, nhưng chúng thực sự hữu ích.

Đây là cách nội suy tuyến tính hoạt động

'---- Phần số

        x = Tầng (rx)
        y = Tầng (ry)

'---- Phần phân số

        fx = rx - x
        fy = ry - y

        gx = 1,0 - fx
        gy = 1.0 - fy

'---- Bình quân gia quyền

        vtl = ArgValues ​​[x, y] * gx * gy 'Trên cùng bên trái
        vtr = ArgValues ​​[x + 1, y] * fx * gy 'Trên cùng bên phải
        vbl = ArgValues ​​[x, y + 1] * gx * fy 'Dưới cùng bên trái
        vbr = ArgValues ​​[x + 1, y + 1] * fx * fy 'Độ cứng đáy

        v = vtl + vtr + vbl + vbr

Bất cứ ai cũng biết tên thông thường cho điều đó?


1
này, bạn không cần phải xin lỗi vì điều gì đó là một cách tiếp cận rất thông minh và có thể cực kỳ hữu ích cho ai đó có vấn đề tương tự sẽ đến đây sau! +1
Marcus Müller

1
@BarsMonster, tôi đang làm tốt. Bạn sẽ muốn cài đặt Gambas (PPA: gambas-team / gambas3) trên hộp Linux của bạn. (Có khả năng, bạn cũng vậy Marcus và Olli, nếu bạn có thể.) Tôi đang làm việc trên một chương trình không chỉ giải quyết vấn đề này mà còn đóng vai trò là cơ sở tốt cho các tác vụ xử lý ảnh khác.
Cedron Dawg

Nhìn về phía trước!
Marcus Müller

@CedronDawg đó được gọi là phép nội suy song tuyến, đây là lý do tại sao , cũng chỉ ra một triển khai thay thế.
Olli Niemitalo

1
@ OlliNiemitalo, Cảm ơn Olli. Trong tình huống này, tôi không nghĩ đi xe đạp sẽ cải thiện kết quả so với song tuyến, thực tế, nó thậm chí có thể gây bất lợi. Sau đó, tôi sẽ chơi xung quanh với các số liệu biến động khác nhau dọc theo đường kính và chức năng cửa sổ có hình dạng khác nhau. Tại thời điểm này, tôi đang nghĩ đến việc sử dụng VonHann ở hai đầu đường kính như mái chèo hoặc "ghế teeter-totter chạm bùn". Đáy phẳng trong đường cong là nơi mà người thợ rèn chưa có mặt đất (cạnh). Một nửa giữa hai góc là một đọc tốt. Các cài đặt hiện tại tốt đến dưới 0,1 độ,
Cedron Dawg

4

Hiệu suất khá cao, nhưng sẽ giúp bạn có được độ chính xác như mong muốn:

  • Cạnh phát hiện hình ảnh
  • Chuyển đổi thành một không gian nơi bạn có đủ pixel cho độ chính xác mong muốn.
  • Vì có đủ đường trực giao; hình ảnh trong không gian chòi sẽ chứa cực đại nằm trên hai dòng. Đây là những dễ dàng phát hiện và cung cấp cho bạn góc mong muốn.

Thật tuyệt, chính xác là cách tiếp cận của tôi: Tôi hơi buồn vì tôi đã không nhìn thấy nó trước khi tôi đi tàu và do đó không kết hợp nó trong câu trả lời của tôi. +1 rõ ràng!
Marcus Müller

4

Tôi đã đi trước và về cơ bản điều chỉnh ví dụ biến đổi Hough của opencv với trường hợp sử dụng của bạn. Ý tưởng này rất hay, nhưng vì hình ảnh của bạn đã có nhiều cạnh do tính chất sắc nét của nó, việc phát hiện cạnh không nên có nhiều lợi ích.

Vì vậy, những gì tôi đã làm ở trên đã nói ví dụ là

  • Bỏ qua phát hiện cạnh
  • phân tách hình ảnh đầu vào của bạn thành các kênh màu và xử lý chúng riêng biệt
  • đếm số lần xuất hiện của các đường trong một góc cụ thể (sau khi định lượng các góc và lấy chúng theo modulo 90 °, vì bạn có nhiều góc vuông)
  • kết hợp các quầy của các kênh màu
  • sửa các phép quay này

Những gì bạn có thể làm để cải thiện hơn nữa chất lượng ước tính (như bạn sẽ thấy bên dưới, dự đoán hàng đầu không đúng - thứ hai là) có thể sẽ chuyển đổi hình ảnh thành hình ảnh thang độ xám thể hiện sự khác biệt thực tế giữa các khác nhau vật liệu tốt nhất - rõ ràng, các kênh RGB không phải là tốt nhất. Bạn là chuyên gia về chất bán dẫn, vì vậy hãy tìm cách kết hợp các kênh màu theo cách tối đa hóa sự khác biệt giữa kim loại hóa và silicon.

Máy tính xách tay jupyter của tôi ở đây . Xem kết quả dưới đây.

Để tăng độ phân giải góc, tăng QUANT_STEPbiến và độ chính xác góc trong hough_transformcuộc gọi. Tôi đã không làm thế, vì tôi muốn mã này được viết trong <20 phút và do đó tôi không muốn đầu tư một phút vào tính toán.

import cv2
import numpy
from matplotlib import pyplot
import collections

QUANT_STEPS = 360*2
def quantized_angle(line, quant = QUANT_STEPS):
    theta = line[0][1]
    return numpy.round(theta / numpy.pi / 2 * QUANT_STEPS) / QUANT_STEPS * 360 % 90

def detect_rotation(monochromatic_img):
    # edges = cv2.Canny(monochromatic_img, 50, 150, apertureSize = 3) #play with these parameters
    lines = cv2.HoughLines(monochromatic_img, #input
                           1, # rho resolution [px]
                           numpy.pi/180, # angular resolution [radian]
                           200) # accumulator threshold – higher = fewer candidates
    counter = collections.Counter(quantized_angle(line) for line in lines)
    return counter
img = cv2.imread("/tmp/HIKRe.jpg") #Image directly as grabbed from imgur.com
total_count = collections.Counter()
for channel in range(img.shape[-1]):
    total_count.update(detect_rotation(img[:,:,channel]))

most_common = total_count.most_common(5)
for angle,_ in most_common:
    pyplot.figure(figsize=(8,6), dpi=100)
    pyplot.title(f"{angle:.3f}°")
    rotation = cv2.getRotationMatrix2D((img.shape[0]/2, img.shape[1]/2), -angle, 1)
    pyplot.imshow(cv2.warpAffine(img, rotation, img.shape[:2]))

đầu ra_4_0

đầu ra_4_1

đầu ra_4_2

đầu ra_4_3

đầu ra_4_4


4

Đây là một bước đi trong phần mở rộng được đề xuất đầu tiên của câu trả lời trước đây của tôi .

Bộ lọc giới hạn đối xứng vòng tròn lý tưởng

Chúng tôi xây dựng một ngân hàng trực giao gồm bốn bộ lọc được giới hạn trong một vòng tròn bán kínhωctrên mặt phẳng tần số. Các đáp ứng xung của các bộ lọc này có thể được kết hợp tuyến tính để tạo thành các hạt nhân phát hiện cạnh định hướng. Một tập hợp các đáp ứng xung bộ lọc trực giao được chuẩn hóa tùy ý có được bằng cách áp dụng hai cặp toán tử vi phân "giống như quả bóng bãi biển" đầu tiên cho đáp ứng xung không gian liên tục của đáp ứng xung bộ lọc lý tưởng đối xứng vòng trònh(x,y):

(1)h(x,y)= =ωc2πx2+y2J1(ωcx2+y2)

(2)h0x(x,y)αddxh(x,y),h0y(x,y)αddyh(x,y),h1x(x,y)α((ddx)3-3ddx(ddy)2)h(x,y),h1y(x,y)α((ddy)3-3ddy(ddx)2)h(x,y)

(3)h0x(x,y)= ={0nếu x= =y= =0,-ωc2xJ2(ωcx2+y2)2π(x2+y2)nếu không thì,h0y(x,y)= =h0x[y,x],h1x(x,y)= ={0nếu x= =y= =0,(ωcx(3y2-x2)(J0(ωcx2+y2)ωcx2+y2(ωc2x2+ωc2y2-24)-số 8J1(ωcx2+y2)(ωc2x2+ωc2y2-6)))2π(x2+y2)7/2nếu không thì,h1y(x,y)= =h1x[y,x],

Ở đâu Jαlà một hàm Bessel của loại thứ tự đầu tiênααcó nghĩa là "tỷ lệ thuận với". Tôi đã sử dụng các truy vấn Wolfram Alpha ( (ᵈ / dx) ³ ; / dx ; / dx (ᵈ / dy) ² ) để thực hiện phân biệt và đơn giản hóa kết quả.

Hạt nhân cắt ngắn trong Python:

import matplotlib.pyplot as plt
import scipy
import scipy.special
import numpy as np

def h0x(x, y, omega_c):
  if x == 0 and y == 0:
    return 0
  return -omega_c**2*x*scipy.special.jv(2, omega_c*np.sqrt(x**2 + y**2))/(2*np.pi*(x**2 + y**2))

def h1x(x, y, omega_c):
  if x == 0 and y == 0:
    return 0
  return omega_c*x*(3*y**2 - x**2)*(scipy.special.j0(omega_c*np.sqrt(x**2 + y**2))*omega_c*np.sqrt(x**2 + y**2)*(omega_c**2*x**2 + omega_c**2*y**2 - 24) - 8*scipy.special.j1(omega_c*np.sqrt(x**2 + y**2))*(omega_c**2*x**2 + omega_c**2*y**2 - 6))/(2*np.pi*(x**2 + y**2)**(7/2))

def rotatedCosineWindow(N):  # N = horizontal size of the targeted kernel, also its vertical size, must be odd.
  return np.fromfunction(lambda y, x: np.maximum(np.cos(np.pi/2*np.sqrt(((x - (N - 1)/2)/((N - 1)/2 + 1))**2 + ((y - (N - 1)/2)/((N - 1)/2 + 1))**2)), 0), [N, N])

def circularLowpassKernel(omega_c, N):  # omega = cutoff frequency in radians (pi is max), N = horizontal size of the kernel, also its vertical size, must be odd.
  kernel = np.fromfunction(lambda x, y: omega_c*scipy.special.j1(omega_c*np.sqrt((x - (N - 1)/2)**2 + (y - (N - 1)/2)**2))/(2*np.pi*np.sqrt((x - (N - 1)/2)**2 + (y - (N - 1)/2)**2)), [N, N])
  kernel[(N - 1)//2, (N - 1)//2] = omega_c**2/(4*np.pi)
  return kernel

def prototype0x(omega_c, N):  # omega = cutoff frequency in radians (pi is max), N = horizontal size of the kernel, also its vertical size, must be odd.
  kernel = np.zeros([N, N])
  for y in range(N):
    for x in range(N):
      kernel[y, x] = h0x(x - (N - 1)/2, y - (N - 1)/2, omega_c)
  return kernel

def prototype0y(omega_c, N):  # omega = cutoff frequency in radians (pi is max), N = horizontal size of the kernel, also its vertical size, must be odd.
  return prototype0x(omega_c, N).transpose()

def prototype1x(omega_c, N):  # omega = cutoff frequency in radians (pi is max), N = horizontal size of the kernel, also its vertical size, must be odd.
  kernel = np.zeros([N, N])
  for y in range(N):
    for x in range(N):
      kernel[y, x] = h1x(x - (N - 1)/2, y - (N - 1)/2, omega_c)
  return kernel

def prototype1y(omega_c, N):  # omega = cutoff frequency in radians (pi is max), N = horizontal size of the kernel, also its vertical size, must be odd.
  return prototype1x(omega_c, N).transpose()

N = 321  # Horizontal size of the kernel, also its vertical size. Must be odd.
window = rotatedCosineWindow(N)

# Optional window function plot
#plt.imshow(window, vmin=-np.max(window), vmax=np.max(window), cmap='bwr')
#plt.colorbar()
#plt.show()

omega_c = np.pi/8  # Cutoff frequency in radians <= pi
lowpass = circularLowpassKernel(omega_c, N)
kernel0x = prototype0x(omega_c, N)
kernel0y = prototype0y(omega_c, N)
kernel1x = prototype1x(omega_c, N)
kernel1y = prototype1y(omega_c, N)

# Optional kernel image save
plt.imsave('lowpass.png', plt.cm.bwr(plt.Normalize(vmin=-lowpass.max(), vmax=lowpass.max())(lowpass)))
plt.imsave('kernel0x.png', plt.cm.bwr(plt.Normalize(vmin=-kernel0x.max(), vmax=kernel0x.max())(kernel0x)))
plt.imsave('kernel0y.png', plt.cm.bwr(plt.Normalize(vmin=-kernel0y.max(), vmax=kernel0y.max())(kernel0y)))
plt.imsave('kernel1x.png', plt.cm.bwr(plt.Normalize(vmin=-kernel1x.max(), vmax=kernel1x.max())(kernel1x)))
plt.imsave('kernel1y.png', plt.cm.bwr(plt.Normalize(vmin=-kernel1y.max(), vmax=kernel1y.max())(kernel1y)))
plt.imsave('kernelkey.png', plt.cm.bwr(np.repeat([(np.arange(321)/320)], 16, 0)))

nhập mô tả hình ảnh ở đây
nhập mô tả hình ảnh ở đây
Hình 1. Biểu đồ tỷ lệ 1: 1 được ánh xạ màu của đáp ứng xung bộ lọc giới hạn băng đối xứng, với tần số cắt ωc= =π/số 8. Phím màu: xanh dương: âm tính, trắng: không, đỏ: tối đa.

nhập mô tả hình ảnh ở đâynhập mô tả hình ảnh ở đây
nhập mô tả hình ảnh ở đâynhập mô tả hình ảnh ở đây
nhập mô tả hình ảnh ở đây
Hình 2. Các sơ đồ tỷ lệ 1: 1 được ánh xạ màu của các phản ứng xung được lấy mẫu của các bộ lọc trong ngân hàng bộ lọc, với tần suất cắt ωc= =π/số 8, theo thứ tự: h0x, h0y, h1x, h0y. Phím màu: xanh dương: tối thiểu, trắng: không, đỏ: tối đa.

Máy dò cạnh định hướng có thể được xây dựng như một khoản tiền có trọng số trong số này. Trong Python (tiếp theo):

composite = kernel0x-4*kernel1x
plt.imsave('composite0.png', plt.cm.bwr(plt.Normalize(vmin=-composite.max(), vmax=composite.max())(composite)))
plt.imshow(composite, vmin=-np.max(composite), vmax=np.max(composite), cmap='bwr')
plt.colorbar()
plt.show()

composite = (kernel0x+kernel0y) + 4*(kernel1x+kernel1y)
plt.imsave('composite45.png', plt.cm.bwr(plt.Normalize(vmin=-composite.max(), vmax=composite.max())(composite)))
plt.imshow(composite, vmin=-np.max(composite), vmax=np.max(composite), cmap='bwr')
plt.colorbar()
plt.show()

nhập mô tả hình ảnh ở đâynhập mô tả hình ảnh ở đây
nhập mô tả hình ảnh ở đây
Hình 3. Các hạt nhân phát hiện cạnh có hướng được xây dựng dưới dạng tổng trọng số của các hạt nhân của Hình 2. Phím màu: xanh dương: tối thiểu, trắng: không, đỏ: tối đa.

Các bộ lọc của Hình 3 nên được điều chỉnh tốt hơn cho các cạnh liên tục, so với các bộ lọc gradient (hai bộ lọc đầu tiên của Hình 2).

Bộ lọc Gaussian

Các bộ lọc của Hình 2 có rất nhiều dao động do giới hạn băng tần nghiêm ngặt. Có lẽ một điểm nhìn tốt hơn sẽ là một hàm Gaussian, như trong các bộ lọc đạo hàm Gaussian. Một cách tương đối, chúng dễ dàng hơn nhiều để xử lý toán học. Hãy thử điều đó thay vào đó. Chúng tôi bắt đầu với định nghĩa đáp ứng xung của bộ lọc "low-pass" Gaussian:

(4)h(x,y,σ)= =e-x2+y22σ22πσ2.

Chúng tôi áp dụng các toán tử của phương trình. 2 đếnh(x,y,σ) và chuẩn hóa từng bộ lọc h.. bởi:

(5)--h..(x,y,σ)2dxdy= =1.

(6)h0x(x,y,σ)= =22πσ2ddxh(x,y,σ)= =-2πσ2xe-x2+y22σ2,h0y(x,y,σ)= =h0x(y,x,σ),h1x(x,y,σ)= =23πσ43((ddx)3-3ddx(ddy)2)h(x,y,σ)= =-33πσ4(x3-3xy2)e-x2+y22σ2,h1y(x,y,σ)= =h1x(y,x,σ).

Chúng tôi muốn xây dựng từ đó, như tổng trọng số của chúng, đáp ứng xung của bộ lọc phát hiện cạnh dọc tối đa hóa tính đặc hiệu S đó là độ nhạy trung bình đối với cạnh dọc so với dịch chuyển cạnh có thể S liên quan đến độ nhạy trung bình trên các góc quay cạnh có thể β và dịch chuyển cạnh có thể S:

(7)S= =2π-(-(-Shx(x,y,σ)dx-Shx(x,y,σ)dx)dy)2dS(-ππ-(-(-Shx(cos(β)x-tội(β)y,tội(β)x+cos(β)y)dx-Shx(cos(β)x-tội(β)y,tội(β)x+cos(β)y)dx)dy)2dSdβ).

Chúng ta chỉ cần một tổng số có trọng số là h0x với phương sai σ2h1xvới phương sai tối ưu. Hóa raS được tối đa hóa bởi một phản ứng thúc đẩy:

(số 8)hx(x,y,σ)= =7625-2440561h0x(x,y,σ)-26105-97661h1x(x,y,5σ)= =-(15250-4880561πσ2xe-x2+y22σ2+18305-29284575πσ4(2x3-6xy2)e-x2+y210σ2= =2πσ215250-4880561ddxh(x,y,σ)-100πσ418305-2928183((ddx)3-3ddx(ddy)2)h(x,y,5σ)3.8275359956049814σ2ddxh(x,y,σ)-33.044650082417731σ4((ddx)3-3ddx(ddy)2)h(x,y,5σ),

cũng được chuẩn hóa bởi phương trình. 5. Để các cạnh dọc, bộ lọc này có tính đặc hiệu làS= =10×51/49 + 2 3.661498645, trái ngược với tính đặc hiệu S= =2 của bộ lọc phái sinh Gaussian bậc nhất đối với x. Phần cuối của phương trình. 8 có khả năng chuẩn hóa tương thích với các bộ lọc phái sinh Gaussian 2-d tách rời khỏi Python scipy.ndimage.gaussian_filter:

import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage

sig = 8;
N = 161
x = np.zeros([N, N])
x[N//2, N//2] = 1
ddx = scipy.ndimage.gaussian_filter(x, sigma=[sig, sig], order=[0, 1], truncate=(N//2)/sig)
ddx3 = scipy.ndimage.gaussian_filter(x, sigma=[np.sqrt(5)*sig, np.sqrt(5)*sig], order=[0, 3], truncate=(N//2)/(np.sqrt(5)*sig))
ddxddy2 = scipy.ndimage.gaussian_filter(x, sigma=[np.sqrt(5)*sig, np.sqrt(5)*sig], order=[2, 1], truncate=(N//2)/(np.sqrt(5)*sig))

hx = 3.8275359956049814*sig**2*ddx - 33.044650082417731*sig**4*(ddx3 - 3*ddxddy2)
plt.imsave('hx.png', plt.cm.bwr(plt.Normalize(vmin=-hx.max(), vmax=hx.max())(hx)))

h = scipy.ndimage.gaussian_filter(x, sigma=[sig, sig], order=[0, 0], truncate=(N//2)/sig)
plt.imsave('h.png', plt.cm.bwr(plt.Normalize(vmin=-h.max(), vmax=h.max())(h)))
h1x = scipy.ndimage.gaussian_filter(x, sigma=[sig, sig], order=[0, 3], truncate=(N//2)/sig) - 3*scipy.ndimage.gaussian_filter(x, sigma=[sig, sig], order=[2, 1], truncate=(N//2)/sig)
plt.imsave('ddx.png', plt.cm.bwr(plt.Normalize(vmin=-ddx.max(), vmax=ddx.max())(ddx)))
plt.imsave('h1x.png', plt.cm.bwr(plt.Normalize(vmin=-h1x.max(), vmax=h1x.max())(h1x)))
plt.imsave('gaussiankey.png', plt.cm.bwr(np.repeat([(np.arange(161)/160)], 16, 0)))

nhập mô tả hình ảnh ở đâynhập mô tả hình ảnh ở đâynhập mô tả hình ảnh ở đâynhập mô tả hình ảnh ở đâynhập mô tả hình ảnh ở đây
Hình 4. Các sơ đồ tỷ lệ 1: 1 được ánh xạ màu theo thứ tự: Hàm Gaussian 2-d, đạo hàm của hàm Gaussian đối với x, một toán tử vi phân (ddx)3-3ddx(ddy)2 áp dụng cho hàm Gaussian, bộ lọc phát hiện cạnh dọc có nguồn gốc Gaussian hai thành phần tối ưu hx(x,y,σ)của phương trình 8. Độ lệch chuẩn của mỗi Gaussian làσ= =số 8 ngoại trừ thành phần lục giác trong ô cuối cùng có độ lệch chuẩn 5×số 8. Phím màu: xanh dương: tối thiểu, trắng: không, đỏ: tối đa.

CÒN TIẾP...

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.