Bạn không tìm kiếm các cạnh (= viền giữa các khu vực mở rộng có giá trị xám cao và thấp), bạn đang tìm các đường vân (đường mỏng tối hơn hoặc sáng hơn vùng lân cận của chúng), vì vậy bộ lọc cạnh có thể không lý tưởng: Bộ lọc cạnh sẽ cung cấp cho bạn hai sườn (một ở mỗi bên của dòng) và phản hồi thấp ở giữa dòng:
THÊM : Nếu đã được yêu cầu giải thích sự khác biệt giữa máy dò cạnh và máy dò sườn rõ ràng hơn. Tôi xin lỗi trước nếu câu trả lời này đang trở nên rất dài.
Một máy dò cạnh là (thường) một toán tử đạo hàm đầu tiên: Nếu bạn tưởng tượng hình ảnh đầu vào là một cảnh quan 3D, thì một máy dò cạnh đo độ dốc của độ dốc tại mỗi điểm của cảnh quan đó:
Nếu bạn muốn phát hiện đường viền của vùng sáng hoặc tối kéo dài, điều này là tốt. Nhưng đối với các tĩnh mạch trong hình ảnh của OP, nó sẽ cung cấp cho bạn giống nhau: các đường viền bên trái và bên phải của mỗi tĩnh mạch:
Điều đó cũng giải thích "mẫu đường đôi" trong kết quả dò cạnh Canny:
Vì vậy, làm thế nào để bạn phát hiện những đường mỏng này (tức là những đường vân), sau đó? Ý tưởng là các giá trị pixel có thể được xấp xỉ (cục bộ) theo đa thức bậc 2, tức là nếu hàm hình ảnh là , thì đối với các giá trị nhỏ của và :xgxy
g(x,y)≈12x2∂2g∂x2+xy∂2g∂x∂y+12y2∂2g∂y2+x∂g∂x+y∂g∂y+g(0,0)
hoặc, ở dạng ma trận:
g(x,y)≈12(xy).⎛⎝⎜∂2g∂x2∂2g∂x∂y∂2g∂x∂y∂2g∂y2⎞⎠⎟.(xy)+(xy).(∂g∂x∂g∂y)+g(0,0)
Ma trận đạo hàm bậc hai được gọi là " Ma trận Hessian ". Nó mô tả cấu trúc bậc 2 mà chúng ta quan tâm.⎛⎝⎜∂2g∂x2∂2g∂x∂y∂2g∂x∂y∂2g∂y2⎞⎠⎟
Phần thứ 2 của hàm này có thể được chuyển thành tổng của hai parabon được quay bởi một góc nào đó, bằng cách phân tách ma trận Hessian ở trên thành một phép quay chéo ma trận chéo của nó ( Phân rã ma trận ). Chúng tôi không quan tâm đến việc xoay vòng (chúng tôi muốn phát hiện các đường vân theo bất kỳ hướng nào), vì vậy chúng tôi chỉ quan tâm đến vàλ1x2+λ2y2λ1λ2
Hàm gần đúng này có thể có dạng hình gì? Trên thực tế, không nhiều:
Để phát hiện các đường vân, chúng tôi muốn tìm các khu vực trong hình ảnh trông giống như các ô cuối cùng ở trên, vì vậy chúng tôi đang tìm kiếm các khu vực có giá trị riêng lớn của Hessian (so với giá trị riêng nhỏ). Cách đơn giản nhất để phát hiện đó là chỉ tính toán giá trị riêng tại mỗi pixel - và đó là những gì bộ lọc sườn dưới đây thực hiện.
Một bộ lọc sườn núi có thể sẽ cho kết quả tốt hơn. Tôi đã thử tích hợp sẵn của Mathicala RidgeFilter
(tính toán giá trị riêng của ma trận Hessian ở mỗi pixel) trên hình ảnh của bạn:
Như bạn có thể thấy, chỉ có một đỉnh duy nhất cho mỗi vạch tối mỏng. Sản lượng Binarizing và skeletonizing:
Sau khi cắt tỉa bộ xương và loại bỏ các thành phần nhỏ (nhiễu) khỏi hình ảnh, tôi nhận được bộ xương cuối cùng này:
Mã Mathicala đầy đủ:
ridges = RidgeFilter[ColorNegate@src];
skeleton = SkeletonTransform[Binarize[ridges, 0.007]];
DeleteSmallComponents[Pruning[skeleton, 50], 50]
THÊM VÀO:
Tôi không phải là chuyên gia Matlab, tôi không biết liệu nó có bộ lọc sườn núi tích hợp hay không, nhưng tôi có thể chỉ cho bạn cách triển khai "bằng tay" (một lần nữa, sử dụng Matematica). Như tôi đã nói, bộ lọc sườn núi là giá trị riêng của ma trận Hessian. Tôi có thể tính toán giá trị biểu tượng đó trong Mathicala:
eigenvalue=Last[Eigenvalues[(HxxHxyHxyHyy)]]
=>12(Hxx+Hyy+H2xx+4H2xy−2HxxHyy+H2yy−−−−−−−−−−−−−−−−−−−−−−−√)
Vì vậy, những gì bạn phải làm là tính toán các đạo hàm thứ hai , , (sử dụng sobel hoặc đạo hàm của bộ lọc gaussian) và chèn chúng vào biểu thức trên và bạn đã có bộ lọc sườn núi của mình. H xy H yyHxxHxyHyy