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:
hx[x,y]=⎧⎩⎨⎪⎪0−ω2cxJ2(ωcx2+y2−−−−−−√)2π(x2+y2)if x=y=0,otherwise,hy[x,y]=⎧⎩⎨⎪⎪0−ω2cyJ2(ωcx2+y2−−−−−−√)2π(x2+y2)if x=y=0,otherwise,(1)
Ở đâ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()
Hình 1. Cửa sổ cosin xoay 2 chiều.
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.pi
giữ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(ϕ) và 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()
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)//2
pixel 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 đồ.
π
π2
π4
π8
π16
π32
π64
-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
Có 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:
Zk=(Xk+Yki)4X2k+Y2k−−−−−−−√3=X4k−6X2kY2k+Y4k+(4X3kYk−4XkY3k)iX2k+Y2k−−−−−−−√3,(2)
thỏa mãn |Zk| = =X2k+Y2k-------√ 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:
ϕ =14atan2(ΣkTôi đang(Zk) ,ΣkTái(Zk) )(3)
Ở đâ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:
MSCD =Σk|Zk| ( 1-cos( 4ϕ-atan2( Tôi(Zk) , Re(Zk) ) ) )Σk|Zk|= =Σk|Zk|2(( cos( 4 ϕ ) -Tái(Zk)|Zk|)2+( tội lỗi( 4 ϕ ) -Tôi đang(Zk)|Zk|)2)Σk|Zk|= =Σk( |Zk| -Re(Zk) cos( 4 ϕ ) - Tôi(Zk) tội lỗi( 4 φ ) )Σk|Zk|,(4)
đượ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 mpmath
thí 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:
Zk= =(Xk+YkTôi)4X2k+Y2k-------√2= =X4k- 6X2kY2k+Y4k+ ( 4X3kYk- 4XkY3k) tôiX2k+Y2k,(5)
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 và ωc= π/ 64. Họ sử dụng lớn hơn N
dẫ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()
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 - π/ 4...π/ 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ácx và yđá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:
hx( r , θ ) =hx[ r cos( Θ ) , r sin( θ ) ]hy( r , θ ) =hy[ r cos( Θ ) , r sin( θ ) ]f( r )= =⎧⎩⎨0-ω2cr cos( θ )J2(ωcr )2 πr2nếu r = 0 ,nếu không thì= cos( θ ) f( r ) ,= =⎧⎩⎨0-ω2cr sin( θ )J2(ωcr )2 πr2nếu r = 0 ,nếu không thì= tội lỗi( θ ) f( r ) ,= =⎧⎩⎨0-ω2crJ2(ωcr )2 πr2nếu r = 0 ,nếu không thì,(6)
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:
h ( r , θ , ϕ ) =hx( r , θ - ϕ ) = cos( θ - ϕ ) f( r )(7)
Ý 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 , θ ) và hx( r , θ ), với cos( ϕ ) và tội( ϕ ) như trọng lượng, và đó thực sự là trường hợp:
cos( ϕ )hx( r , θ ) + tội lỗi( ϕ )hy( r , θ ) = cos( ϕ ) cos( θ ) f( r ) + tội lỗi( ϕ ) tội lỗi( θ ) f( r ) = cos( θ - ϕ ) f( r ) = h ( r , θ , ϕ ) .(số 8)
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ó:
x = cos( ϕ )xφ- tội lỗi( ϕ )yφ,y= tội lỗi( ϕ )xφ+ cos( ϕ )yφ(9)
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 x và y:
∂∂xφ= =∂x∂xφ∂∂x+∂y∂xφ∂∂y= =∂( cos( ϕ )xφ- tội lỗi( ϕ )yφ)∂xφ∂∂x+∂( tội lỗi( ϕ )xφ+ cos( ϕ )yφ)∂xφ∂∂y= cos( ϕ )∂∂x+ tội lỗi( ϕ )∂∂y(10)
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.
limh → 0Σ4 N+ 1N= 0( - 1)nf( x+hcos(2 πn4 N+ 2) ,y+ h tội lỗi(2 πn4 N+ 2) )h2 N+ 1,limh → 0Σ4 N+ 1N= 0( - 1)nf( x+htội lỗi(2 πn4 N+ 2) ,y+ h cos(2 πn4 N+ 2) )h2 N+ 1(11)
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.