Обнаружение и разделение квадратных объектов в изображении

#matlab #image-processing

#matlab #обработка изображений

Вопрос:

У меня есть следующее исходное изображение со случайными квадратами черного цвета:

исходное изображение

Вопросы:

  1. Как я могу определить, сколько в нем квадратов? И как получить информацию о ширине, высоте и начальной позиции (x, y) для каждого квадрата?

  2. Как я могу отделиться от объединения квадрата в один квадрат (может отдельно становиться 2 квадрата / 3 квадрата / больше / автоматически), как показано ниже:

я уже делаю несколько простых симуляций в соответствии с этой проблемой, вот мой код в matlab, теперь этот код полностью работает для обнаружения square, но когда я меняю источник изображения на образец файла с square, он выдает некоторую ошибку


%% First Initialisation
tic; % Start timer.
clc; % Clear command window.
clear;
close all; % Close all figure windows except those created by imtool.
imtool close all;
clearvars; % Get rid of variables from prior run of this m-file.
workspace; % Make sure the workspace panel is showing.

 %Source
%RGB = imread('Result.png');
RGB = [
     0   0   0   0 255 255 255  0   0;
     0   0   0   0 255 255 255  0   0;
     0   0   0   0 255 255 255 255 255;
    255 255 255 255 0   0   0   0   0;
    255 255 255 255 0   0   0   0   0;
     0   0   0   0 255 255 255  0   0;
     0   0   0   0 255 255 255  0   0;
    255 255 255 255 0   0   0   0   0;
    255 255 255 255 0   0   0   0   0;
    ];
figure;
imshow(RGB);
caption = sprintf('Source Image');
title(caption, 'FontSize', 13);


%%Make It White
white=Make_Image_White(RGB);
figure;
imshow(white);
caption = sprintf('White Image');
title(caption, 'FontSize', 13);
RGB=white;

%save x,y location
[yy xx] = find( RGB == 0 );

%%Prepare Struct Variabel to Save vertical Black Line
RectLine=struct('Line',[],'PosX',[],'PosY',[],'Width',[],'Height',[]);

%start counting vertical black line
startNew=0;
line=0;
limit=0;

for repeat = 1 : size(find(RGB==0),1)
    if startNew==0
        line=line 1;
        fprintf('Start The-%d Line....n',line);
        startNew=1;
        Height=0;
        if limit==1
            PosY=yy(repeat-1);
            PosX=xx(repeat-1);
            fprintf(' =-=-=-=-=-=-=-=-=-=-=-=-Continue n'); % Message sent to command window.                
            fprintf(' New Box Detection ==> %d n', line); % Message sent to command window.                
            fprintf('   Position (x,y) => (%d,%d) n', PosX, PosY); % Message sent to command window.                
            fprintf('   Counting X-%d : (x,y) -> [ %d , %d ] n', repeat-1, xx(repeat-1), yy(repeat-1)); % Message sent to command window.                
%            fprintf('xx(repeat)==tempX amp;amp; tempY 1==yy(repeat)nr');
%            fprintf('%d==%d amp;amp; %d==%dnr',xx(repeat-1),xx(repeat-1),tempY-1,yy(repeat-1));
            fprintf(' 1.* add height 1 ==> Counting X-%d : (x,y) -> [ %d , %d ] n', repeat-1, xx(repeat-1), yy(repeat-1)); % Message sent to command window.                
            tempX=xx(repeat);
            Height=Height 1;    
            tempY=yy(repeat-1);
            startNew=startNew 1;
        else
            PosY=yy(repeat);
            PosX=xx(repeat);
            tempX=0;
            tempY=0;
        end
        Width=1;
        startY=PosY;
        startX=PosX;        
        if limit==0
            fprintf(' =-=-=-=-=-=-=-=-=-=-=-=- Realn'); % Message sent to command window.                
            fprintf(' New Line Detection ==> %d n', line); % Message sent to command window.                
            fprintf('   Position (x,y) => [ %d , %d ] n', PosX, PosY); % Message sent to command window.                
            fprintf('   Counting X-%d : (x,y) -> [ %d , %d ] n', repeat, xx(repeat), yy(repeat)); % Message sent to command window.                
        end

    end

    if (RGB(yy(repeat),xx(repeat),1)==0)
%            fprintf(' =-=Tes Titik [%d,%d] n',xx(repeat),yy(repeat)); % Message sent to command window.                
        if tempX==0
%            fprintf('%d==%d amp;amp; %d==%dn',xx(repeat),tempX,tempY 1,yy(repeat));
            fprintf(' %d.a add height 1 ==> Counting X-%d : (x,y) -> [ %d , %d ] n', startNew, repeat,xx(repeat), yy(repeat)); % Message sent to command window.                
            tempX=xx(repeat);
            Height=Height 1;    
            tempY=tempY 1;
            startNew=startNew 1;
        elseif xx(repeat)==tempX amp;amp; tempY 1==yy(repeat)
 %           fprintf('xx(repeat)==tempX amp;amp; tempY 1==yy(repeat)nr');
 %           fprintf('%d==%d amp;amp; %d==%dnr',xx(repeat),tempX,tempY 1,yy(repeat));
            fprintf(' %d.b add height 1 ==> Counting X-%d : (x,y) -> [ %d , %d ] n', startNew, repeat,xx(repeat), yy(repeat)); % Message sent to command window.                
            Height=Height 1;     
            tempX=xx(repeat);
            tempY=tempY 1;
            startNew=startNew 1;
            if repeat == size(find(RGB==0),1)
                RectLine(line).Line=line;
                RectLine(line).PosX=PosX;
                RectLine(line).PosY=PosY;
                RectLine(line).Width=Width;
                RectLine(line).Height=Height;                
            end
        else
%            fprintf('xx(repeat)==tempX amp;amp; tempY 1==yy(repeat)n');
%            fprintf('%d==%d amp;amp; %d==%dn',xx(repeat),tempX,tempY 1,yy(repeat));
            limit=1;            
            startNew=0;
            RectLine(line).Line=line;
            RectLine(line).PosX=PosX;
            RectLine(line).PosY=PosY;
            RectLine(line).Width=Width;
            RectLine(line).Height=Height;                
            tempX=xx(repeat);    
        end
    end;
end

fprintf('nnStart Combine Line become Boxn');
RectBox=struct('Box',[],'PosX',[],'PosY',[],'Width',[],'Height',[]);
startBox=1;
for line = size(RectLine,1) : size(RectLine,2)
    fprintf(' ************************** Line %dn',line);
    Width=0;
    fprintf('  Start Point (%d,%d) Height = %d ** Width=%dn',RectLine(line).PosX,RectLine(line).PosY,RectLine(line).Height,Width);
    if startBox>1
        exist=0;
        for cek = size(RectBox,1) : size(RectBox,2)
            if (RectLine(line).PosX >= RectBox(cek).PosX) amp;amp; (RectLine(line).PosX <= RectBox(cek).PosX RectBox(cek).Width-1) ...
                amp;amp; (RectLine(line).PosY >= RectBox(cek).PosY) amp;amp; (RectLine(line).PosY <= RectBox(cek).PosY RectBox(cek).Height-1)
                if (RectLine(line).Height == RectBox(cek).Height)
                    exist=1;  
                elseif (RectLine(line).Height > RectBox(cek).Height)
                    RectLine(size(RectBox,2) 1).Line=size(RectBox,2) 1;
                    RectLine(size(RectBox,2) 1).PosX=RectLine(line).PosX;
                    RectLine(size(RectBox,2) 1).PosY=RectLine(line).PosY;
                    RectLine(size(RectBox,2) 1).Width=1;
                    RectLine(size(RectBox,2) 1).Height=RectLine(line).Height - RectBox(cek).Height;
                end
            end

            if exist==1
                fprintf('  ** Line Start From (%d,%d) -> Already Exist On Box %dn',RectLine(line).PosX,RectLine(line).PosY,cek);
                break;
            end
        end
        if exist==0
            for x=RectLine(line).PosX : size(RGB,2)
                fprintf('      Cek Find Start Position looping at %d -> (%d,%d)n',x,RectLine(line).PosX,RectLine(line).PosY);
                fprintf('      find(RGB(%d:%d,%d:%d))n',RectLine(line).PosY,RectLine(line).PosY RectLine(line).Height-1,RectLine(line).PosX,x);
                if find(RGB(RectLine(line).PosY:RectLine(line).PosY RectLine(line).Height-1,RectLine(line).PosX:x))
                    fprintf('  --> Save %d - Boxn',startBox);
                    fprintf('      Start Position (%d,%d)n',RectLine(line).PosX,RectLine(line).PosY);
                    fprintf('      Width=%d ** Height=%dn',Width,RectLine(line).Height);
                    RectBox(startBox).Box=startBox;
                    RectBox(startBox).PosX=RectLine(line).PosX;
                    RectBox(startBox).PosY=RectLine(line).PosY;
                    RectBox(startBox).Width=Width;
                    RectBox(startBox).Height=RectLine(line).Height;    
                    startBox=startBox 1;
                    break;
                elseif x==size(RGB,2)
                    Width=Width 1;
                    fprintf(' b--> Save %d - Boxn',startBox);
                    fprintf('      Start Position (%d,%d)n',RectLine(line).PosX,RectLine(line).PosY);
                    fprintf('      Width=%d ** Height=%dn',Width,RectLine(line).Height);
                    RectBox(startBox).Box=startBox;
                    RectBox(startBox).PosX=RectLine(line).PosX;
                    RectBox(startBox).PosY=RectLine(line).PosY;
                    RectBox(startBox).Width=Width;
                    RectBox(startBox).Height=RectLine(line).Height;    
                    startBox=startBox 1;
                    break;
                else
                    up0=0;
                    down0=0;
%                    fprintf('Y RGB=%d,%d n',RectLine(line).PosY-1,x);
                    if RectLine(line).PosY>1
                        if (RGB(RectLine(line).PosY-1,x)>0)
                            up0=1;
                        else
                        end     

                    else
                        up0=1;
                    end

 %                   fprintf('X RGB=%d,%d n',RectLine(line).PosY RectLine(line).Height,x);
                    if (RectLine(line).PosY RectLine(line).Height) <= size(RGB,1)
                        if (RGB(RectLine(line).PosY RectLine(line).Height,x)>0)
                            down0=1;
                        end   
                    else
                        down0=1;
                    end
%                    fprintf('posy=%d,x=%d,height=%d-->tot=%dn',RectLine(line).PosY, x, RectLine(line).Height, RectLine(line).PosY   RectLine(line).Height);
%                    fprintf('Status Up=%d ---- Down=%dn',up0,down0);
                    if (up0==1) amp;amp; (down0==1)
                        Width=Width 1;
                        fprintf('    Position (%d,%d) -> Width 1 => %dn',x,RectLine(line).PosY,Width);                        
                    else
                        fprintf('c  --> Save %d - Boxn',startBox);
                        fprintf('      Start Position (%d,%d)n',RectLine(line).PosX,RectLine(line).PosY);
                        fprintf('      Width=%d ** Height=%dn',Width,RectLine(line).Height);
                        RectBox(startBox).Box=startBox;
                        RectBox(startBox).PosX=RectLine(line).PosX;
                        RectBox(startBox).PosY=RectLine(line).PosY;
                        RectBox(startBox).Width=Width;
                        RectBox(startBox).Height=RectLine(line).Height;    
                        startBox=startBox 1;
                        break;
                    end
                end
            end
        end
    else
         for x=RectLine(line).PosX : size(RGB,2)
             fprintf('      Cek Find Start Position looping at %d -> (%d,%d)n',x,RectLine(line).PosX,RectLine(line).PosY);
             fprintf('      find(RGB(%d:%d,%d:%d))n',RectLine(line).PosY,RectLine(line).PosY RectLine(line).Height-1,RectLine(line).PosX,x);

            if find(RGB(RectLine(line).PosY:RectLine(line).PosY RectLine(line).Height-1,RectLine(line).PosX:x))
                fprintf('  --> Save %d - Boxn',startBox);
                fprintf('      Start Position (%d,%d)n',RectLine(line).PosX,RectLine(line).PosY);
                fprintf('      Width=%d ** Height=%dn',Width,RectLine(line).Height);
                RectBox(startBox).Box=startBox;
                RectBox(startBox).PosX=RectLine(line).PosX;
                RectBox(startBox).PosY=RectLine(line).PosY;
                RectBox(startBox).Width=Width;
                RectBox(startBox).Height=RectLine(line).Height;    
                startBox=startBox 1;
                break;
            elseif x==size(RGB,2)
                Width=Width 1;
                fprintf(' b--> Save %d - Boxn',startBox);
                fprintf('      Start Position (%d,%d)n',RectLine(line).PosX,RectLine(line).PosY);
                fprintf('      Width=%d ** Height=%dn',Width,RectLine(line).Height);
                RectBox(startBox).Box=startBox;
                RectBox(startBox).PosX=RectLine(line).PosX;
                RectBox(startBox).PosY=RectLine(line).PosY;
                RectBox(startBox).Width=Width;
                RectBox(startBox).Height=RectLine(line).Height;    
                startBox=startBox 1;
                break;
            else
                Width=Width 1;
                fprintf('    Position (%d,%d) -> Width 1 => %dn',x,RectLine(line).PosY,Width);
            end
        end
    end
end

RectBox 
  

ожидаемый результат выглядит следующим образом

http://imgur.com/a/0ypc3

Комментарии:

1. Привет, ваш вопрос слишком широкий, вы должны быть более точными! Для первой части (пункт 1) вашего вопроса вы можете разделить свою матрицу на более мелкие части (каждая часть = 1 большой пиксель) с помощью функции reshape , а затем проверить для каждой части (размер AxAx3), если сумма (штук (:)) == 0 или нет.

2. Всегда ли квадраты одинакового размера на каждом изображении?

3. для размера квадрата мы точно не знаем размер, поэтому, возможно, какой-то квадрат будет иметь 2 или более раз меньший размер в направлении x или y, или, может быть, в обоих направлениях

4. Нет, я имел в виду, всегда ли размер наименьшего квадрата фиксирован от одного изображения к следующему изображению? Изображение всегда находится в сетке 10x10 или иногда может быть 20x20?

5. все наименьшие квадраты не имеют фиксированного размера, это будет зависеть от того, сколько фрагментов строк и столбцов генерируется. у меня есть еще один пример изображения генерации, подобный этому! [Другой пример] ( imgur.com/a/EBpPK )

Ответ №1:

Черные квадраты полностью черные (значение RGB [0,0,0]), поэтому вы можете установить пороговое значение с очень низким значением, и у вас будет маска, которая сохраняет только квадраты (существуют лучшие подходы, но вы можете начать с этого)

Затем вы можете запустить некоторый детектор краев, чтобы сохранить только края.

Если затем вы добавите пиксели по столбцам и строкам, пики будут соответствовать тем столбцам и строкам, которые принадлежат краям квадратов.

Поскольку вы знаете, что квадраты имеют одинаковый размер, вы можете экстраполировать недостающие столбцы и строки на случай, если у некоторых из них нет черного квадрата, который дает вам пик.

Другим подходом было бы выполнение преобразования Хафа и сохранение только идеально горизонтальных и вертикальных линий, которые будут соответствовать краям квадратов.

Ответ №2:

Чтобы ответить на первую часть вашего вопроса:

 I = imread('866PX.jpg');

%Split your image into smaller elements
I2 = mat2cell(I,repmat(40,10,1),repmat(42,10,1),3); 

%if an element has more than half of its pixels = 0 then this element is a black square.
for ii = 1:10
    for jj = 1:10
        ind(ii,jj) = sum(I2{ii,jj}(:)>0)>(numel(I2{1,1})/2);
    end
end

imshow(ind)
%number of black square
nbr = sum(~ind(:))
  

Результат:

введите описание изображения здесь

Комментарии:

1. спасибо #obchardon, этот код отлично подходит для решения этой проблемы, но, как я уже сказал, он будет иметь случайный размер квадрата, как в этом примере imgur.com/a/EBpPK , этот код не может решить эту проблему

Ответ №3:

Я не использую Matlab, но вот несколько идей, реализованных с помощью ImageMagick (только в командной строке), которые, надеюсь, вы сможете адаптировать.

Если вы порогируете свое изображение и обнаруживаете края, например:

 convert chess.png -threshold 10 -edge 1 result.png
  

введите описание изображения здесь

Если вы теперь удалите, используя прямоугольный структурирующий элемент размером 1x3, вы удалите горизонтальные линии и получите это:

 convert chess.png -threshold 10 -edge 1 -morphology erode rectangle:1x3 result.png
  

введите описание изображения здесь

Теперь вы можете сканировать изображение по строкам и искать белые пиксели, которые подскажут вам, где находятся края блоков.

Затем вы можете применить ту же технику к изображению другим способом:

 convert chess.png -threshold 10 -edge 1 -morphology erode rectangle:3x1 result.png
  

введите описание изображения здесь

Затем вы можете определить свой размер квадрата по наименьшему промежутку между белыми пикселями, обнаруженными по вертикали или горизонтали.

Затем вы можете применить "Анализ больших двоичных объектов" или "Анализ подключенных компонентов" к изображению с пороговым значением, чтобы найти черные квадраты:

 convert chess.png -threshold 10 -negate       
    -define connected-components:verbose=true 
    -connected-components 4 output.png
  

Пример вывода

 Objects (id: bounding-box centroid area mean-color):
  0: 420x399 0 0 144.3,166.8 78660 srgb(0,0,0)
  3: 240x285 180 114 287.5,256.0 34200 srgb(255,255,255)
  6: 120x171 300 171 374.5,256.0 13680 srgb(0,0,0)
  5: 120x114 0 171 69.5,237.0 10260 srgb(255,255,255)
  2: 120x114 300 57 369.5,104.0 10260 srgb(255,255,255)
  1: 120x57 120 0 179.5,28.0 6840 srgb(255,255,255)
  4: 60x57 300 114 329.5,142.0 3420 srgb(0,0,0)
  7: 60x57 60 342 89.5,370.0 3420 srgb(255,255,255)
  8: 60x57 180 342 209.5,370.0 3420 srgb(255,255,255)
  9: 60x57 240 342 269.5,370.0 3420 srgb(0,0,0)
  

И каждая строка сообщает нам высоту и ширину большого двоичного объекта и его верхнего левого угла. Я нарисую эти капли на исходном изображении красным цветом:

введите описание изображения здесь

На самом деле вы также можете определить размер квадратов в исходном изображении, посмотрев на GCD размеров больших двоичных объектов - все они кратны примерно 60.

Итак, теперь вы также можете выполнить вторую часть своего вопроса. Посмотрите на большой двоичный объект размером 120x57, который должен быть размером 2 квадрата на 1 квадрат.

Ответ №4:

код не в matlab, но его можно легко преобразовать в него, поскольку я использую только самые основные операции, отличные от процедур рисования.

Ключевая идея состоит в том, чтобы просто сначала определить минимальный размер прямоугольника. Черные на самом деле не квадраты, а прямоугольники. Вы делаете это, сканируя по горизонтали и вертикали и находя непрерывную область черных областей. Минимальная длина пикселя в каждом направлении формирует минимальный размер прямоугольника.

 import cv2
import numpy as np
im=cv2.imread('QfOab0P.png')
cv2.imshow('im',im)

# the following deal with jpeg artifacts and squares will contain clean squares at the end
squares = np.zeros((im.shape[0],im.shape[1]),np.uint8)

for y in xrange(im.shape[0]):
    for x in xrange(im.shape[1]):
        if (sum(im[y,x,:])<40):
            squares[y,x]=255
squares=cv2.erode(squares,np.ones((5,5),np.uint8))
squares=cv2.dilate(squares,np.ones((5,5),np.uint8))


# we need to detect minimum size of the square
szx = {}
szy={}
# scan vertically
for x in xrange(im.shape[1]):
    sz = 0
    for y in xrange(im.shape[0]):
        if squares[y,x]==255:
            sz  = 1
        elif sz>0:
            if sz not in szy:
                szy[sz]=0
            szy[sz] =1
            sz = 0
    if sz > 0:
        if sz not in szy:
            szy[sz]=0
        szy[sz] =1
        sz = 0
# scan horizontally
for y in xrange(im.shape[0]):
    sz = 0
    for x in xrange(im.shape[1]):
        if squares[y,x]==255:
            sz  = 1
        elif sz>0:
            if sz not in szx:
                szx[sz]=0
            szx[sz] =1
            sz = 0
    if sz > 0:
        if sz not in szx:
            szx[sz]=0
        szx[sz] =1
        sz = 0
szx = {k:v for k,v in szx.iteritems() if v>10} # dicard spurious small values caused by imperfect thresholding
szy = {k:v for k,v in szy.iteritems() if v>10}

sqsz = [min(szx.keys()),min(szy.keys())]
# now we may need to refine the values so that we can differentiate between 56 vs 57 in this example
sqsz[0] = int(round(float(im.shape[1])/int(im.shape[1]/float(sqsz[0]))))
sqsz[1] = int(round(float(im.shape[0])/int(im.shape[0]/float(sqsz[1]))))
print sqsz
if im.shape[0]%sqsz[1]!=0 or im.shape[1]%sqsz[0]!=0:
    print 'square size detection failed'
# since you know the minimum square size, you can do whatever you wish
# here's an example where the black squares are numbered
# of course you can get fancy and move the blocks around if you want to
sq_cnt =0
for yi in xrange(im.shape[0]/sqsz[1]):
    for xi in xrange(im.shape[1]/sqsz[0]):
        x = xi*sqsz[0]
        y = yi*sqsz[1]
        patch = im[y:y sqsz[1],x:x sqsz[0],:]
        if patch.sum()<100000:#this should be ideally equal to zero but due to jpeg,black is not always black
            #this is a black square
            cv2.putText(im,str(sq_cnt),(x sqsz[0]/2,y sqsz[1]/2),cv2.FONT_HERSHEY_SIMPLEX,0.5,(255,255,255),2)
            sq_cnt =1
        cv2.rectangle(im,(x,y),(x sqsz[0],y sqsz[1]),(255,255,0))
cv2.imshow('sq',squares)
cv2.imshow('with_squares',im)
cv2.imwrite('with_squares.jpg',im)
cv2.waitKey(0)
  

введите описание изображения здесь

Комментарии:

1. это то, что я сейчас ищу, какую программу вы используете для этой проблемы? спасибо за решение, я попробую решить другую квадратную проблему

2. это на python с использованием opencv и numpy

3. я уже пытаюсь использовать ваш код, результат для другого исходного изображения обнаружен не полностью, это результат для вашего кода для другого изображения imgur.com/a/sijzN

4. Хорошо, есть две проблемы: 1) если вы посмотрите на изображение с пороговым значением, вы увидите, что оно не идеально, есть небольшие небольшие обнаружения, это легко исправить, отбросив менее часто встречающиеся ширины и высоты 2) вторая проблема - это случай, когда изображение неравномерно делится наколичество квадратов, чтобы определить две возможные длины, 56 и 57 для высоты. В этом случае я делаю предположение, что правильный ответ - тот, который ближе к целому значению. 400/56 == 7,142 и 400/57 == 7,017. Таким образом, 57 рассматривается как правильный.

Ответ №5:

о, слава БОГУ, я уже нашел проблему. это из-за разного размера изображения, в моем небольшом моделировании я использую 1 измерение, делаю, когда хочу получить позицию (x, y), я просто делаю

 %save x,y location
[yy xx] = find( RGB == 0 );
  

итак, когда я меняю источник на образец изображения, этот образец изображения имеет 3 измерения, когда я выполняю эту команду

 %save x,y location
[yy xx] = find( RGB == 0 );
  

в части xx оно будет умножено на 3, оно станет xx * 3
, поэтому я добавляю перед ним некоторую команду

 %change dimension 3 become 1
if size(RGB,3) > 1 
    RGB = rgb2gray(RGB); 
end

%save x,y location
[yy xx dimension] = find( RGB == 0 );
  

после этого вся программа работает нормально.

кстати, выполнение этого кода требует времени, потому что он проверяет один за другим все пиксели, у кого-нибудь есть какое-либо решение, чтобы сократить время, пожалуйста?

спасибо за вашу помощь