Tìm mô hình giống ngựa vằn trong ảnh (Phát hiện đường tâm rìa có cấu trúc ánh sáng từ ảnh)


12

Tôi đang làm việc trong một dự án nơi rìa được chiếu theo một chủ đề, và một bức ảnh được chụp. Nhiệm vụ là tìm các đường tâm của rìa, đại diện cho toán học, đường cong 3D của giao điểm giữa mặt phẳng rìa và bề mặt chủ thể.

Bức ảnh là một PNG (RGB) và các nỗ lực trước đây đã sử dụng thang độ xám sau đó tạo ra sự khác biệt để có được một bức ảnh đen trắng giống như "ngựa vằn", từ đó dễ dàng tìm thấy điểm giữa của mỗi cột pixel của mỗi rìa. Vấn đề là, bằng cách ngưỡng và cũng bằng cách lấy chiều cao trung bình của cột pixel rời rạc, chúng ta có một số mất chính xác và lượng tử hóa, điều không mong muốn chút nào.

Ấn tượng của tôi, bằng cách nhìn vào các hình ảnh, là các đường trung tâm có thể liên tục hơn (nhiều điểm hơn) và mượt mà hơn (không bị lượng tử hóa) nếu chúng được phát hiện trực tiếp từ hình ảnh không ngưỡng (bằng RGB hoặc thang độ xám), bằng một số phương pháp quét thống kê (một số lũ lụt / lặp đi lặp lại, bất cứ điều gì).

Dưới đây là hình ảnh mẫu thực tế:

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

Bất kỳ đề nghị sẽ được nhiều đánh giá cao!


nó rất thú vị Nhưng nhân tiện, tôi đang thực hiện một số nghiên cứu sử dụng dải màu để phát hiện vật thể 3d. Vì sử dụng sọc màu, bạn có thể dễ dàng tìm thấy sự tương ứng của từng dải từ máy chiếu. Ngoài ra, bằng cách sử dụng lượng giác, thông tin 3d có thể được tính toán. Làm thế nào để bạn tìm thấy sự tương ứng nếu màu sắc giống nhau? Tôi đoán dự án của bạn cũng là về tái thiết 3d?

@johnyoung: Vui lòng không thêm nhận xét làm câu trả lời. Tôi nhận ra bạn cần danh tiếng trước khi bạn có thể bình luận, nhưng xin vui lòng kiềm chế quá trình hành động hiện tại của bạn. Tôi đề nghị đặt câu hỏi (liên quan) của riêng bạn hoặc trả lời câu hỏi của người khác để tăng đại diện của bạn.
Peter K.

Xin lỗi vì có thêm một câu hỏi thay vì đưa ra câu trả lời, Trong phương pháp dịch pha, chúng tôi tính toán pha ở mỗi pixel trong hình ảnh được chiếu, nhưng ở đây tại sao chúng tôi cần tìm ra đường trung tâm của rìa, có thể câu hỏi của tôi là rất ngớ ngẩn nhưng tôi không không, vì vậy xin vui lòng tel cho tôi lý do chính xác. Bạn có thể xóa câu hỏi của mình sau khi đưa ra câu trả lời

Đây là những phương pháp khác nhau. Tôi đang mô hình hóa một loạt các mặt phẳng hình học bằng cách chiếu một loạt các sọc trắng (mỗi cái tạo thành một "mặt phẳng" trong không gian 3D). Vì vậy, tôi cần tìm đường trung tâm của rìa, vì các mặt phẳng không có độ dày. Chắc chắn tôi có thể thực hiện phân tích dịch pha, nhưng có một vấn đề: hình chiếu của tôi là nhị phân (sọc đen và trắng xen kẽ), cường độ không thay đổi hình sin và vì vậy hiện tại tôi không thể thực hiện chuyển pha (và hiện tại không cần ).
heltonbiker

Câu trả lời:


13

Tôi đề nghị các bước sau:

  1. Tìm một ngưỡng để tách tiền cảnh khỏi nền.
  2. Đối với mỗi đốm màu trong hình ảnh nhị phân (một sọc ngựa vằn), đối với mỗi đốm x, hãy tìm trung tâm có trọng số (theo cường độ pixel) ytheo hướng.
  3. Có thể, làm mịn các ygiá trị, để loại bỏ nhiễu.
  4. Kết nối các (x,y)điểm bằng cách lắp một số loại đường cong. Bài viết này có thể giúp bạn. Bạn cũng có thể phù hợp với một đa thức cấp cao, mặc dù nó tệ hơn, theo ý kiến ​​của tôi.

Đây là mã Matlab hiển thị các bước 1,2 và 4. Tôi đã bỏ qua lựa chọn ngưỡng tự động. Thay vào đó tôi chọn thủ công th=40:

Đây là những đường cong được tìm thấy bằng cách tìm trung bình có trọng số trên mỗi cột: nhập mô tả hình ảnh ở đây

Đây là những đường cong sau khi lắp một đa thức: nhập mô tả hình ảnh ở đây

Đây là mã:

function Zebra()
    im = imread('http://i.stack.imgur.com/m0sy7.png');
    im = uint8(mean(im,3));

    th = 40;
    imBinary = im>th;
    imBinary = imclose(imBinary,strel('disk',2));
    % figure;imshow(imBinary);
    labels = logical(imBinary);
    props =regionprops(labels,im,'Image','Area','BoundingBox');

    figure(1);imshow(im .* uint8(imBinary));
    figure(2);imshow(im .* uint8(imBinary));

    for i=1:numel(props)
        %Ignore small ones
        if props(i).Area < 10
            continue
        end
        %Find weighted centroids
        boundingBox = props(i).BoundingBox;
        ul = boundingBox(1:2)+0.5;
        wh = boundingBox(3:4);
        clipped = im( ul(2): (ul(2)+wh(2)-1), ul(1): (ul(1)+wh(1)-1) );
        imClip = double(props(i).Image) .* double(clipped);
        rows = transpose( 1:size(imClip,1) );
        %Weighted calculation
        weightedRows  = sum(bsxfun(@times, imClip, rows),1) ./ sum(imClip,1);
        %Calculate x,y
        x = ( 1:numel(weightedRows) ) + ul(1) - 1;
        y = ( weightedRows ) + ul(2) - 1;
        figure(1);
        hold on;plot(x,y,'b','LineWidth',2);
        try %#ok<TRYNC>
            figure(2);
            [xo,yo] = FitCurveByPolynom(x,y);
            hold on;plot(xo,yo,'g','LineWidth',2);
        end
        linkaxes( cell2mat(get(get(0,'Children'),'Children')) )
    end        
end

function [xo,yo] = FitCurveByPolynom(x,y)
   p = polyfit(x,y,15); 
   yo = polyval(p,x);
   xo = x;
end

Tôi thấy điều này rất thú vị. Tôi sử dụng Python, nhưng dù sao tôi cũng sẽ phải nghiên cứu lý do của tất cả những điều này. Là một nhận xét độc lập, tôi có xu hướng không thực hiện xử lý hình ảnh cổ điển (trực tiếp trên các thùng chứa hình ảnh được lượng tử hóa như mảng uint8), mà thay vào đó tải mọi thứ vào bộ nhớ dưới dạng mảng nổi trước khi áp dụng các thao tác. Ngoài ra, tôi rất ngạc nhiên với kết quả từ nửa dưới của hình ảnh của bạn, các đường màu xanh không chạy dọc theo đường giữa rìa dự kiến ​​... (?). Cảm ơn vì bây giờ, tôi sẽ mang lại một số phản hồi ngay khi nhận được kết quả!
heltonbiker

@heltonbiker, vui lòng kiểm tra câu trả lời cập nhật. Bạn nói đúng về dấu phẩy động, tôi đã sử dụng nó khi tôi chuyển đổi sang double. Về kết quả ở nửa dưới, tôi cần kiểm tra, nó có thể là một lỗi phần mềm
Andrey Rubshtein

1
@rctonbiker, xong rồi. Đó thực sự là một lỗi liên quan đến lập chỉ mục dựa trên 1.
Andrey Rubshtein

Tuyệt vời! Thật tuyệt vời Với kỹ thuật này, và với mục đích của tôi, việc làm mịn không chỉ không cần thiết mà còn có hại. Cảm ơn rất nhiều vì sự quan tâm của bạn!
heltonbiker

3

Tôi sẽ không sử dụng hình ảnh RGB. Hình ảnh màu thường được thực hiện bằng cách đặt "Bộ lọc Bayer" trên cảm biến máy ảnh, điều này thường làm giảm độ phân giải bạn có thể đạt được.

Nếu bạn sử dụng hình ảnh thang độ xám, tôi nghĩ rằng các bước bạn mô tả (hình ảnh "ngựa vằn", tìm đường giữa) là một khởi đầu tốt. Bước cuối cùng, tôi sẽ

  • Lấy từng điểm trong đường giữa bạn tìm thấy
  • lấy giá trị xám của các pixel trong dòng "ngựa vằn" bên trên và bên dưới
  • điều chỉnh một parabol cho các giá trị xám này bằng cách sử dụng các ô vuông nhỏ nhất
  • đỉnh của parabola này là một ước tính được cải thiện của vị trí đường giữa

Những suy nghĩ tốt đẹp. Tôi dự định sử dụng một số loại parabola hoặc spline dọc theo các giá trị đỉnh của từng cột pixel, nhưng tôi vẫn tự hỏi liệu tôi nên kiểm tra một cột pixel hay thay vào đó là một "vùng" pixel dọc theo dòng ... Gonna chờ thêm một chút cho nhiều câu trả lời hơn Cảm ơn ngay bây giờ!
heltonbiker

@heltonbiker - dưới dạng thử nghiệm nhanh, chỉ sử dụng kênh màu xanh lá cây. Bình thường có gấp 2 lần số pixel màu xanh lục trên cảm biến màu và nó ít bị xen kẽ hơn màu đỏ và màu xanh
Martin Beckett

@MartinBeckett Cảm ơn sự quan tâm của bạn, tôi đã phân tích từng kênh và thực sự kênh màu xanh lá cây dường như được giải quyết nhiều hơn so với, màu đỏ. Tuy nhiên, việc vẽ các giá trị cường độ của các mặt cắt dọc cho mỗi kênh, "mẫu sọc" dường như không thay đổi quá nhiều giữa các kênh và tôi hiện đang trộn lẫn chúng khi chuyển đổi sang thang độ xám. Mặc dù vậy, tôi vẫn có kế hoạch nghiên cứu kết hợp tuyến tính tốt nhất giữa các kênh để có kết quả tương phản tốt nhất, HOẶC để thu được hình ảnh đã có trong thang độ xám. Cảm ơn một lần nữa!
heltonbiker

3

Đây vẫn là một giải pháp thay thế cho vấn đề của bạn bằng cách mô hình hóa câu hỏi của bạn dưới dạng 'vấn đề tối ưu hóa đường dẫn'. Mặc dù nó phức tạp hơn so với giải pháp tạo đường cong đơn giản và sau đó là đường cong, nhưng nó mạnh mẽ hơn trong thực tế.

Từ mức rất cao, chúng ta nên xem hình ảnh này như một biểu đồ, trong đó

  1. mỗi pixel hình ảnh là một nút trên biểu đồ này

  2. mỗi nút được kết nối với một số nút khác, được gọi là hàng xóm và định nghĩa kết nối này thường được gọi là cấu trúc liên kết của biểu đồ này.

  3. mỗi nút có trọng số (tính năng, chi phí, năng lượng hoặc bất cứ điều gì bạn muốn gọi nó), phản ánh khả năng nút này nằm trong một đường trung tâm tối ưu mà chúng tôi đang tìm kiếm.

Miễn là chúng ta có thể mô hình hóa khả năng này, thì vấn đề tìm kiếm 'đường trung tâm của rìa' sẽ trở thành vấn đề tìm đường dẫn tối ưu cục bộ trên biểu đồ , có thể giải quyết hiệu quả bằng lập trình động, ví dụ thuật toán Viterbi.

Dưới đây là một số ưu điểm của việc áp dụng phương pháp này:

  1. tất cả các kết quả của bạn sẽ liên tục (không giống như phương pháp ngưỡng có thể chia một đường trung tâm thành từng mảnh)

  2. rất nhiều quyền tự do để xây dựng một biểu đồ như vậy, bạn có thể chọn các tính năng khác nhau và cấu trúc liên kết biểu đồ.

  3. kết quả của bạn là tối ưu theo nghĩa tối ưu hóa đường dẫn

  4. giải pháp của bạn sẽ mạnh mẽ hơn chống lại nhiễu, vì miễn là nhiễu được phân bổ đều giữa tất cả các pixel, các đường dẫn tối ưu đó vẫn ổn định.

Dưới đây là một minh chứng ngắn về ý tưởng trên. Vì tôi không sử dụng bất kỳ kiến ​​thức nào trước đó để chỉ định các nút bắt đầu và kết thúc có thể là gì, tôi chỉ cần giải mã wrt mỗi nút bắt đầu có thể. Đường dẫn Viterbi được giải mã

Đối với các kết thúc mờ, nguyên nhân là do chúng ta đang tìm kiếm các đường dẫn tối ưu cho mọi nút kết thúc có thể. Kết quả là, mặc dù đối với một số nút nằm trong vùng tối, đường dẫn được tô sáng vẫn là đường dẫn tối ưu cục bộ.

Đối với đường dẫn mờ, bạn có thể làm mịn nó sau khi bạn tìm thấy nó hoặc sử dụng một số tính năng được làm mịn thay vì cường độ thô.

Có thể khôi phục các đường dẫn một phần bằng cách thay đổi các nút bắt đầu và kết thúc.

Sẽ không khó để cắt tỉa những con đường tối ưu cục bộ không mong muốn này. Bởi vì chúng tôi có khả năng của tất cả các đường dẫn sau khi giải mã viterbi và bạn có thể sử dụng nhiều kiến ​​thức trước đó (ví dụ: chúng tôi thấy đúng là chúng tôi chỉ cần một đường dẫn tối ưu cho những người chia sẻ cùng một nguồn.)

Để biết thêm chi tiết, bạn có thể tham khảo bài báo.

 Wu, Y.; Zha, S.; Cao, H.; Liu, D., & Natarajan, P.  (2014, February). A Markov Chain Line Segmentation Method for Text Recognition. In IS&T/SPIE 26th Annual Symposium on Electronic Imaging (DRR), pp. 90210C-90210C.

Dưới đây là một đoạn mã python ngắn sử dụng để tạo biểu đồ trên.


import cv2
import numpy as np
from matplotlib import pyplot
# define your image path
image_path = ;
# read in an image
img = cv2.imread( image_path, 0 );
rgb = cv2.imread( image_path, -1 );

# some feature to reflect how likely a node is in an optimal path
img = cv2.equalizeHist( img ); # equalization
img = img - img.mean(); # substract DC
img_pmax = img.max(); # get brightest intensity
img_nmin = img.min(); # get darkest intensity
# express our preknowledge
img[ img > 0 ] *= +1.0  / img_pmax; 
img[ img = 1 :
    prev_idx = vt_path[ -1 ].astype('int');
    vt_path.append( path_buffer[ prev_idx, time ] );
    time -= 1;
vt_path.reverse();    
vt_path = np.asarray( vt_path ).T;

# plot found optimal paths for every 7 of them
pyplot.imshow( rgb, 'jet' ),
for row in range( 0, h, 7 ) :
    pyplot.hold(True), pyplot.plot( vt_path[row,:], c=np.random.rand(3,1), lw = 2 );
pyplot.xlim( ( 0, w ) );
pyplot.ylim( ( h, 0 ) );

Đây là một cách tiếp cận rất thú vị. Tôi thú nhận chủ đề "đồ thị" đã bị che khuất đối với tôi cho đến gần đây khi (trong cùng dự án này) tôi chỉ có thể giải quyết vấn đề khác bằng cách sử dụng biểu đồ. Sau khi tôi "hiểu", tôi nhận ra các thuật toán đường dẫn ngắn nhất này có thể mạnh đến mức nào. Ý tưởng của bạn rất thú vị và không phải là tôi không thể thực hiện lại cho ý tưởng này nếu tôi có nhu cầu / cơ hội. Cảm ơn rât nhiều.
heltonbiker

Đối với kết quả hiện tại của bạn, theo kinh nghiệm của tôi, có lẽ tốt hơn là làm mịn hình ảnh trước bằng bộ lọc gaussian và / hoặc trung vị, trước khi xây dựng biểu đồ. Điều này sẽ cho các dòng mượt mà hơn (và chính xác hơn). Ngoài ra, một mẹo có thể là mở rộng vùng lân cận để cho phép "nhảy trực tiếp" qua hai hoặc nhiều pixel (tối đa đến một giới hạn nhất định, giả sử, 8 hoặc 10 pixel). Tất nhiên nên chọn hàm chi phí phù hợp, nhưng tôi nghĩ nó dễ điều chỉnh.
heltonbiker

Ồ vâng. Tôi chỉ đơn giản là chọn một cái gì đó trong tay, bạn chắc chắn có thể sử dụng các chức năng cấu trúc và năng lượng khác. Trên thực tế, khung này cũng có thể đào tạo được. Cụ thể, bạn bắt đầu với cường độ thô, giải mã cho các đường dẫn tối ưu, chỉ chọn các nút tối ưu có độ tin cậy cao và bằng cách này bạn sẽ có được 'dữ liệu được gắn nhãn'. Với phần nhỏ này của dữ liệu được gắn nhãn tự động, bạn có thể tìm hiểu nhiều loại điều hữu ích.
cạm bẫy

3

Thiết nghĩ tôi nên đăng câu trả lời của mình vì nó hơi khác so với các phương pháp khác. Tôi đã thử điều này trong Matlab.

  • tổng hợp tất cả các kênh và tạo một hình ảnh, vì vậy tất cả các kênh đều có trọng số như nhau
  • thực hiện đóng hình thái và lọc Gaussian trên hình ảnh này
  • đối với mỗi cột của hình ảnh kết quả, tìm cực đại cục bộ và tạo một hình ảnh
  • tìm các thành phần được kết nối của hình ảnh này

Một nhược điểm tôi thấy ở đây là cách tiếp cận này sẽ không thực hiện tốt đối với một số định hướng của các sọc. Trong trường hợp đó, chúng tôi phải sửa hướng của nó và áp dụng quy trình này.

Đây là mã Matlab:

im = imread('m0sy7.png');
imsum = sum(im, 3); % sum all channels
h = fspecial('gaussian', 3);
im2 = imclose(imsum, ones(3)); % close
im2 = imfilter(im2, h); % smooth
% for each column, find regional max
mx = zeros(size(im2));
for c = 1:size(im2, 2)
    mx(:, c) = imregionalmax(im2(:, c));
end
% find connected components
ccomp = bwlabel(mx);

Ví dụ: nếu bạn lấy cột giữa của hình ảnh, cấu hình của nó sẽ trông như thế này: (màu xanh lam là cấu hình. Màu xanh lá cây là cực đại cục bộ) hồ sơ giữa và cực đại địa phương

Và hình ảnh chứa cực đại cục bộ cho tất cả các cột trông như thế này: nhập mô tả hình ảnh ở đây

Dưới đây là các thành phần được kết nối (mặc dù một số sọc bị hỏng, hầu hết chúng có một vùng liên tục):

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


Đây thực sự là những gì chúng ta đang làm bây giờ, với sự khác biệt duy nhất là làm thế nào để tìm cực đại cục bộ cho mỗi cột pixel: chúng ta sử dụng phép nội suy parabol để tìm đỉnh chính xác của parabol đi qua pixel với giá trị tối đa và các lân cận trên và dưới của nó . Điều này cho phép s cho kết quả là "giữa" pixel, đại diện tốt hơn cho độ mịn tinh tế của các đường. Cảm ơn câu trả lời của bạn!
heltonbiker
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.