Обработка изображений — реализация фильтра Собеля

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

Я прочитал несколько документов и PDF-файлов, но они посвящены многим темам, которые, как мне кажется, могут мне не понадобиться для моей задачи.

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

Спасибо

РЕДАКТИРОВАТЬ:

Всем спасибо :) Результат нашей работы можно скачать здесь.


person Ahmad Siavosh    schedule 23.07.2013    source источник
comment
Голосование за закрытие в качестве ресурса rec.   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 01.04.2016


Ответы (8)


Это довольно просто, вам просто нужно свернуть изображение с помощью фильтра Собеля. Фильтр Собеля имеет два ядра: ядро ​​x-направления и ядро ​​y-направления. Ядро в направлении x обнаруживает горизонтальные края, а ядро ​​в направлении y обнаруживает вертикальные края.

ядро x-направления (размер 3x3)

float kernelx[3][3] = {{-1, 0, 1}, 
                       {-2, 0, 2}, 
                       {-1, 0, 1}};

ядро Y-направления

float kernely[3][3] = {{-1, -2, -1}, 
                        {0,  0,  0}, 
                        {1,  2,  1}};

Чтобы вычислить свертку в пикселе (x, y), определите размер окна, равный размеру ядра (исходный код для вычисления величины по x и величины по y идентичен):

double magX = 0.0; // this is your magnitude

for(int a = 0; a < 3; a++)
{
    for(int b = 0; b < 3; b++)
    {            
        int xn = x + a - 1;
        int yn = y + b - 1;

        int index = xn + yn * width;
        magX += image[index] * kernelx[a][b];
    }
 }

Обратите внимание, что вход представляет собой изображение в градациях серого, и его можно представить в виде одномерного массива double (это всего лишь уловка, поскольку значение пикселя в координате (x, y) можно получить с помощью index = [x + y * width] )

Чтобы вычислить величину в пикселях (x, y) с учетом magX и magY :

mag = sqrt( magX^2 + magY^2)

person azer89    schedule 24.07.2013
comment
есть ли смысл комбинировать горизонтальное и вертикальное ядра, сделав одно реальной частью, а другое мнимой, а затем вы можете найти величину, получив абс (результат)? - person endolith; 10.04.2014
comment
@azer89 Я сомневаюсь, что простое умножение image и kernelx сработает, так как нам нужна свертка, верно? - person Shailesh; 19.08.2017
comment
Посмотрите в этом блоге: двухмерная свертка - person Shailesh; 19.08.2017

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

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

Пост описывает в деталях (не слишком много), как реализовать фильтр, и делится исходным кодом Ruby для демонстрационных целей:

require 'chunky_png'

class ChunkyPNG::Image
  def at(x,y)
    ChunkyPNG::Color.to_grayscale_bytes(self[x,y]).first
  end
end

img = ChunkyPNG::Image.from_file('engine.png')

sobel_x = [[-1,0,1],
           [-2,0,2],
           [-1,0,1]]

sobel_y = [[-1,-2,-1],
           [0,0,0],
           [1,2,1]]

edge = ChunkyPNG::Image.new(img.width, img.height, ChunkyPNG::Color::TRANSPARENT)

for x in 1..img.width-2
  for y in 1..img.height-2
    pixel_x = (sobel_x[0][0] * img.at(x-1,y-1)) + (sobel_x[0][1] * img.at(x,y-1)) + (sobel_x[0][2] * img.at(x+1,y-1)) +
              (sobel_x[1][0] * img.at(x-1,y))   + (sobel_x[1][1] * img.at(x,y))   + (sobel_x[1][2] * img.at(x+1,y)) +
              (sobel_x[2][0] * img.at(x-1,y+1)) + (sobel_x[2][1] * img.at(x,y+1)) + (sobel_x[2][2] * img.at(x+1,y+1))

    pixel_y = (sobel_y[0][0] * img.at(x-1,y-1)) + (sobel_y[0][1] * img.at(x,y-1)) + (sobel_y[0][2] * img.at(x+1,y-1)) +
              (sobel_y[1][0] * img.at(x-1,y))   + (sobel_y[1][1] * img.at(x,y))   + (sobel_y[1][2] * img.at(x+1,y)) +
              (sobel_y[2][0] * img.at(x-1,y+1)) + (sobel_y[2][1] * img.at(x,y+1)) + (sobel_y[2][2] * img.at(x+1,y+1))

    val = Math.sqrt((pixel_x * pixel_x) + (pixel_y * pixel_y)).ceil
    edge[x,y] = ChunkyPNG::Color.grayscale(val)
  end
end

edge.save('engine_edge.png')

Ввод/вывод:

person karlphillip    schedule 04.10.2014
comment
Просто случайно вы скопировали и вставили приведенный выше код неправильно (или это было неправильно в сообщении в блоге, и теперь оно исправлено?). sobel_x[0][4] явно никогда не сработает. Должно быть [0][1][1][1][2][1] и т. д. - person Doug; 20.06.2015

Оператор Sobel На странице Википедии хорошо описано, как это сделать. Существуют и другие операторы, такие как Roberts cross и Prewitt

Используя операцию свертки, вы можете переключить подход, изменив матрицу ядра. Ниже вам может помочь реализация Sobel и Convolution с использованием Marvin Framework.

Собель:

public class Sobel extends MarvinAbstractImagePlugin{

    // Definitions
    double[][] matrixSobelX = new double[][]{
            {1,     0,  -1},
            {2,     0,  -2},
            {1,     0,  -1}
    };
    double[][] matrixSobelY = new double[][]{
            {-1,    -2,     -1},
            {0,     0,      0},
            {1,     2,      1}
    };

    private MarvinImagePlugin   convolution;

    public void load(){
        convolution = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.convolution.jar");
    }

    public MarvinAttributesPanel getAttributesPanel(){
        return null;
    }
    public void process
    (
        MarvinImage imageIn, 
        MarvinImage imageOut,
        MarvinAttributes attrOut,
        MarvinImageMask mask, 
        boolean previewMode
    )
    {
        convolution.setAttribute("matrix", matrixSobelX);
        convolution.process(imageIn, imageOut, null, mask, previewMode);
        convolution.setAttribute("matrix", matrixSobelY);
        convolution.process(imageIn, imageOut, null, mask, previewMode);
    }
}

Свертка:

public class Convolution extends MarvinAbstractImagePlugin{

    private MarvinAttributesPanel   attributesPanel;
    private MarvinAttributes        attributes;

    public void process
    (
        MarvinImage imageIn, 
        MarvinImage imageOut,
        MarvinAttributes attributesOut,
        MarvinImageMask mask, 
        boolean previewMode
    )
    {
        double[][] matrix = (double[][])attributes.get("matrix");

        if(matrix != null && matrix.length > 0){
            for(int y=0; y<imageIn.getHeight(); y++){
                for(int x=0; x<imageIn.getWidth(); x++){
                    applyMatrix(x, y, matrix, imageIn, imageOut);
                }
            }
        }
    }

    private void applyMatrix
    (
        int x,
        int y,
        double[][] matrix,
        MarvinImage imageIn,
        MarvinImage imageOut
    ){

        int nx,ny;
        double resultRed=0;
        double resultGreen=0;
        double resultBlue=0;

        int xC=matrix[0].length/2;
        int yC=matrix.length/2;

        for(int i=0; i<matrix.length; i++){
            for(int j=0; j<matrix[0].length; j++){
                if(matrix[i][j] != 0){      
                    nx = x + (j-xC);
                    ny = y + (i-yC);

                    if(nx >= 0 && nx < imageOut.getWidth() && ny >= 0 && ny < imageOut.getHeight()){

                        resultRed   +=  (matrix[i][j]*(imageIn.getIntComponent0(nx, ny)));
                        resultGreen +=  (matrix[i][j]*(imageIn.getIntComponent1(nx, ny)));
                        resultBlue  +=  (matrix[i][j]*(imageIn.getIntComponent2(nx, ny)));
                    }


                }



            }
        }

        resultRed   = Math.abs(resultRed);
        resultGreen = Math.abs(resultGreen);
        resultBlue = Math.abs(resultBlue);

        // allow the combination of multiple appications
        resultRed   += imageOut.getIntComponent0(x,y);
        resultGreen += imageOut.getIntComponent1(x,y);
        resultBlue  += imageOut.getIntComponent2(x,y);

        resultRed   = Math.min(resultRed, 255);
        resultGreen = Math.min(resultGreen, 255);
        resultBlue  = Math.min(resultBlue, 255);

        resultRed   = Math.max(resultRed, 0);
        resultGreen = Math.max(resultGreen, 0);
        resultBlue  = Math.max(resultBlue, 0);

        imageOut.setIntColor(x, y, imageIn.getAlphaComponent(x, y), (int)resultRed, (int)resultGreen, (int)resultBlue);
    }

    public void load(){
        attributes = getAttributes();
        attributes.set("matrix", null);
    }

    public MarvinAttributesPanel getAttributesPanel(){
        if(attributesPanel == null){
            attributesPanel = new MarvinAttributesPanel();
            attributesPanel.addMatrixPanel("matrixPanel", "matrix", attributes, 3, 3);
        }
        return attributesPanel;
    }

}
person Gabriel Ambrósio Archanjo    schedule 25.07.2013
comment
Привет, я новичок в этом. Я создал основной класс и получил эту ошибку. Можете ли вы сказать мне, как написать для него основной класс? Собел a = новый Собел(); MarvinImage imgIn = MarvinImageIO.loadImage(unnamed2.jpg); MarvinImage imgOut = MarvinImageIO.loadImage(test.jpg); a.process(imgIn, imgOut); Исключение в основном потоке java.lang.NullPointerException в Sobel.process(Sobel.java:41) - person ; 22.04.2014

Gx оценивает градиент в направлении x (столбцы), а Gy оценивает градиент в направлении y (строки). Таким образом, Gy обнаруживает горизонтальные линии, а Gx обнаруживает вертикальные линии.

person Marina Tolkachov    schedule 01.04.2016
comment
Это должен быть комментарий - person ketan; 01.04.2016

Конечно, вы можете использовать OpenCV для этого:

import cv2
import numpy as np

img = cv2.imread(INPUT_IMAGE)
img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY).astype(float)

edge_x = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
edge_y = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)    
edge = np.sqrt(edge_x**2 + edge_y**2)    # image can be normalized to 
                                         # fit into 0..255 color space
cv2.imwrite(OUTPUT_IMAGE, edge)

Ввод, вывод:

person Andriy Makukha    schedule 07.07.2018

Все вышеупомянутые шаги в файле R markdown. Надеюсь, это сделает его более наглядным и понятным. Мне нужно было реализовать фильтр Собеля, и эта страница помогла мне понять концепции, но у меня были некоторые проблемы с этим. Так что, если собрать все в одном месте, надеюсь, это поможет.

http://rpubs.com/ghub_24/420754

person ghub24    schedule 17.09.2018

Я использую Octave 4.4.1 для обработки изображений и определения границ. Octave — это программное обеспечение с открытым исходным кодом, которое обеспечивает функциональность MatLab. Я использовал следующий код из "https://in.mathworks.com/". Предположим, что изображение 'k1.jpg' Для реализации оператора края Собеля i=imread('k1.jpg');//Чтобы прочитать изображение pkg load image//Загрузка панели инструментов для обработки изображений g=rgb2gray(i)//Преобразование изображение в градациях серого S=edge(g,'Sobel');//Применение детектора границ Собеля к g imshow(S);//Для отображения изображения Исходное изображение Сегментированное изображение

person VinaPani_Namoh    schedule 05.03.2020

Вы можете сделать это в R с растровым пакетом (ориентированным на географические данные)

library(raster)
sobel <- function(r) {
    fy <- matrix(c(1,0,-1,2,0,-2,1,0,-1)/4, nrow=3)
    fx <- matrix(c(-1,-2,-1,0,0,0,1,2,1)/4 , nrow=3)
    rx <- focal(r, fx)
    ry <- focal(r, fy)
    sqrt(rx^2 + ry^2)
}

b <- brick("https://i.stack.imgur.com/Bnxa6.jpg")
plotRGB(b)
s <- stack(lapply(1:3, function(i) sobel(b[[i]])))
plotRGB(s)

# or
ss <- mean(s)
plot(ss, col=gray(1:10/10))

# or
bb <- mean(b)
sss <- sobel(bb)
plot(sss, col=gray(1:10/10))
person Robert Hijmans    schedule 15.04.2020