1
votes

I want to replicate the GIMP Filter > Edge Decect > Difference of Gaussians in C++ using opencv.

I found this simple code for DOG implementation bu I want the same result of GIMP with the two params Raidus1 and Radius2.

Mat g1, g2, result;
Mat img = imread("test.png", CV_LOAD_IMAGE_COLOR); 
GaussianBlur(img, g1, Size(1,1), 0);
GaussianBlur(img, g2, Size(3,3), 0);
result = g1 - g2;

How can add the 2 radius params to the implementation?

Sample input image enter image description here

Params enter image description here

Output enter image description here

If can help this is the link to the C implementation of the filter

https://gitlab.gnome.org/GNOME/gimp/blob/master/plug-ins/common/edge-dog.c

2
Would you care to give a sample input and corresponding filtered output image so I can pull my remaining hair out?Mark Setchell
@MarkSetchell sample added. Please dont sacrifice you hair :)Fabiano Taioli

2 Answers

1
votes

I don't have the answer, but have run out of hair - see comment. I have been working on this and have some code that doesn't work, but somebody cleverer than me who doesn't feel like writing code, may be able to see what is wrong, so I thought I would share what I have. I am not interested in any points, so anyone is welcome to take and adapt this and show a working answer. Fine by me if we find a solution. I did it in Python but I am sure we can easily adapt any Python to C++ if we get something that works.

#!/usr/bin/env python3

import numpy as np
import math
import cv2

def radius2stdev(radius):
    """
    Return std deviation corresponding to a given radius.
    I got this from: https://gitlab.gnome.org/GNOME/gimp/blob/master/plug-ins/common/edge-dog.c
    """
    stdev  = math.sqrt (-(radius * radius) / (2 * math.log (1.0 / 255.0)));
    return stdev

# Load image, make float and scale to range 0..1
im = cv2.imread("image.jpg",cv2.IMREAD_COLOR).astype(np.float)
im = im/255.0

stdev1  = radius2stdev(22.0)
stdev2  = radius2stdev(5.0)

print('Stdev1: {}'.format(stdev1))
print('Stdev2: {}'.format(stdev2))

# Generate the two Gaussians and their difference
# I believe OpenCV calculates the size of the kernel to match the std dev if you pass no kernel size
# See https://docs.opencv.org/3.4.1/d4/d86/group__imgproc__filter.html#gaabe8c836e97159a9193fb0b11ac52cf1

g1 = cv2.GaussianBlur(im,(0,0),stdev1,stdev1)
g2 = cv2.GaussianBlur(im,(0,0),stdev2,stdev2)
result = g1 -g2

# Multiply back up by 255 and save as PNG
result = (result * 255).astype(np.uint8)
cv2.imwrite("result.png", result)

# Normalize and save normalised too
resultn = cv2.normalize(result,None,alpha=0,beta=255,norm_type=cv2.NORM_MINMAX)
cv2.imwrite("result-n.png", resultn)

The standard deviations are printed out like this:

Stdev1: 6.608505869104614
Stdev2: 1.5019331520692305

I believe the 22,000 and 5,000 shown for your radii are just a result of your internationalisation and these correspond to 22.0 and 5.0 in US/UK format.


I also had an attempt with ImageMagick on the command-line and got something vaguely similar though I am not sure what that proves:

magick image.jpg -morphology Convolve DoG:0,20,5 -evaluate multiply 6 result.jpg

enter image description here

0
votes

It looks like missing a line. From lines 729-730 of edge-dog.c:

  radius = fabs (radius) + 1.0;
  std_dev = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0)));

Gimp then computes the Gaussian as list of integers in make_curve(std_dev, ...). Note