Introducing a New High-Quality Edge Detector
A Significantly Improved Sobel Edge Detection
Published on February 2, 2022 | Last Modified on July 17, 2025
Edge detection is a fundamental tool in image processing, playing a crucial role as a preprocessing step in pattern recognition. We present a novel approach to this field: the VC-Filter.
Limitations of Traditional Sobel Filtering
The Sobel kernel Gy, shown below, is designed to detect horizontal edges in an image.
1 0 -1 1 2 1
Gx = 2 0 -2 Gy = 0 0 0
1 0 -1 -1 -2 -1However, as the orientation of the edge deviates from the horizontal direction, the sensitivity of the Sobel filter rapidly decreases. Vertical edges, in particular, often fall into the filter’s blind zone and become nearly invisible — as shown in the figure below:
Two Domains of Image Filtering
Sobel filtering (and image processing in general) can be performed in two primary ways:
- Spatial Domain: The image is convolved directly with the Sobel kernel. OpenCV provides a ready-made solution:
cv2.Sobel(image, cv.CV_64F, 0, 1, ksize=3) - Frequency Domain: The image and the filter are transformed into the frequency domain using the Fourier transform. Their spectra are multiplied, and the result is then transformed back using the inverse Fourier transform.
Our Innovation: Rotating the Filter Spectrum
We propose a simple yet powerful modification: rotate the Sobel filter’s frequency spectrum to align it with the desired edge orientation.
Here, rot(b, alpha) signifies the rotation of the Sobel kernel’s spectrum by an angle alpha. This rotation effectively shifts the filter’s “blind zone,” tuning the modified Sobel filter to a new direction specified by alpha.
The modified procedure, applied to the image above, demonstrates the repositioning of the blind zone for alpha values of 0, 30, 60, and 90 degrees. The magnitude of the rotated kernel spectrum, rot(b, alpha), is displayed on the right.
Overcoming Blind Zones with VC-Filter
While individual Sobel filters (even modified ones) cannot entirely eliminate blind zones, a parallel application of multiple modified Sobel filters — which we call the VC-Filter — can.
The VC-Filter operates by combining the results of a set of such filters, each tuned to a different angle (e.g., 0, 12, 24, and so on). By summarizing these parallel outputs, we achieve a comprehensive and perfect outline image.
The VC-Filter is essentially a collection of orientation-selective filters that work simultaneously.
Comparative Analysis: VC-Filter vs. OpenCV’s Recommended Solution
Let’s compare the widely used OpenCV edge detection pipeline (which typically applies GaussianBlur followed by Laplacian) against the VC-Filter (available on GitHub).
Test 1: Detecting Blurred Edges
Consider the Unsplash portrait where the photographer intentionally defocused the left side. OpenCV’s solution, being insensitive to out-of-focus blurred edges, renders them almost invisible in its contour output.
In contrast, the VC-Filter successfully identifies all edges, including the blurred ones. This is because multiple accumulations are a well-known technique for detecting weak signals. Furthermore, the quality of the VC-Filter’s output can be further enhanced by adjusting contrast and the number of primitive filters used.
Test 2: False Edge Detection
A more serious issue with OpenCV’s default pipeline is the detection of non-existent edges.
In the figure below:
- Original image (shapes)
- Outline produced by OpenCV
- Outline produced by VC-Filter
See how the OpenCV filter, unlike the vc-filter, improperly duplicates all lines.
Technical Note on VC-Filter
The VC-Filter is a high-quality, general-purpose edge detector constructed from any set of orientation-selective filters acting in parallel. For example, it can be based on the Sobel kernel (Gx ), Prewitt, or Roberts kernels. The key requirement for the primitive filter is its orientation-selective property, which necessitates a transition to the frequency domain for modification.
# USING OPENCV RECOMMENDED FILTER
# https://docs.opencv.org/3.4/d5/db5/tutorial_laplace_operator.html
import cv2 as cv
from pathlib import Path
def opencv_recommended_filter(image):
# Remove noise by blurring with a Gaussian filter
image_gauss = cv.GaussianBlur(image, (3, 3), cv.BORDER_DEFAULT)
# Convert the image to grayscale
image_in_gray = cv.cvtColor(image_gauss, cv.COLOR_BGR2GRAY)
# Apply Laplace function
image_out_gray = cv.Laplacian(image_in_gray, cv.CV_16S, ksize=3)
# converting back to uint8
abs_image_out_gray = cv.convertScaleAbs(image_out_gray)
return abs_image_out_gray
""" Get input image """
path_in = Path.cwd() / 'DATA' / 'image_in.png'
image_in = cv.imread(str(path_in), cv.IMREAD_UNCHANGED)
""" MAIN """
# -------------------------------------------------------------
edges_opencv = opencv_recommended_filter(image_in)
path_opencv = Path.cwd() / 'DATA' / 'edges_opencv.png'
cv.imwrite(str(path_opencv), edges_opencv)
# -------------------------------------------------------------
# USING VC-FILTER
# https://github.com/boriskravtsov/vc-filter
import cv2 as cv
from pathlib import Path
from time import perf_counter
from utils_directory import init_directory
import vc_filter
# Pick one of the image file
# -------------------------------------
image = 'face 2048x2048.jpg'
# image = 'Puerto Rico.jpg'
# image = 'fan.png'
# image = 'shapes.png'
# image = 'test_1.png'
# image = 'test_2.png'
# -------------------------------------
def main():
init_directory('images_out')
filename = Path(image)
name = filename.stem
extension = filename.suffix
path_in = str(Path.cwd() / 'images_in' / str(name + extension))
image_in = cv.imread(path_in, cv.IMREAD_UNCHANGED)
# ---------------------------------------------------------
start_time = perf_counter()
image_edges = vc_filter.apply(image_in, 2.7) # brightness parameter
time_spent = perf_counter() - start_time
# ---------------------------------------------------------
path_edges /=
str(Path.cwd() / 'images_out' / str(name + '_edges' + extension))
cv.imwrite(str(path_edges), image_edges)
print(f'\nTime spent: {time_spent:.3f} sec')
if __name__ == "__main__":
main()Biological Inspiration: The Visual Cortex Connection
More than 60 years ago, D. Hubel and T. Wiesel (Nobel Prize in Physiology or Medicine) discovered a set of orientation-selective neurons in the visual cortex, each tuned to different angles. While the exact purpose of these neurons in the brain remains a subject of research, we hypothesize that these orientation-selective neurons collectively compose the contour image of visible objects, much like the set of modified primitive filters in our VC-Filter method.
The name “VC-Filter” (Visual Cortex Filter) is inspired by the structure and function of the Visual Cortex, mirroring how the brain processes edge information through parallel, orientation-selective mechanisms.
