OpenCV: Laplace Operator

| OpenCV 3.4.2 Open Source Computer Vision

Laplace Operator

Prev Tutorial: Sobel Derivatives
Next Tutorial: Canny Edge Detector

## Goal

In this tutorial you will learn how to:

- Use the OpenCV function Laplacian() to implement a discrete analog of the Laplacian operator.

## Theory

1. In the previous tutorial we learned how to use the Sobel Operator. It was based on the fact that in the edge area, the pixel intensity shows a "jump" or a high variation of intensity. Getting the first derivative of the intensity, we observed that an edge is characterized by a maximum, as it can be seen in the figure:

2. And...what happens if we take the second derivative?

   You can observe that the second derivative is zero! So, we can also use this criterion to attempt to detect edges in an image. However, note that zeros will not only appear in edges (they can actually appear in other meaningless locations); this can be solved by applying filtering where needed.

### Laplacian Operator

1. From the explanation above, we deduce that the second derivative can be used to detect edges. Since images are "*2D*", we would need to take the derivative in both dimensions. Here, the Laplacian operator comes handy.

2. The Laplacian operator is defined by:

   \[Laplace(f) = \dfrac{\partial^{2} f}{\partial x^{2}} + \dfrac{\partial^{2} f}{\partial y^{2}}\]

3. The Laplacian operator is implemented in OpenCV by the function Laplacian() . In fact, since the Laplacian uses the gradient of images, it calls internally the Sobel operator to perform its computation.

## Code

1. What does this program do?
   - Loads an image
   - Remove noise by applying a Gaussian blur and then convert the original image to grayscale
   - Applies a Laplacian operator to the grayscale image and stores the output image
   - Display the result in a window

C++

1. The tutorial code's is shown lines below. You can also download it from here
   #include "opencv2/imgproc.hpp"
   #include "opencv2/imgcodecs.hpp"
   #include "opencv2/highgui.hpp"
   using namespace cv;
   int main( int argc, char** argv )
   {
   // Declare the variables we are going to use
   Mat src, src_gray, dst;
   int kernel_size = 3;
   int scale = 1;
   int delta = 0;
   int ddepth = CV_16S;
   const char* window_name = "Laplace Demo";
   const char* imageName = argc >=2 ? argv[1] : "../data/lena.jpg";
   src = imread( imageName, IMREAD_COLOR ); // Load an image
   // Check if image is loaded fine
   if(src.empty()){
   printf(" Error opening image\n");
   printf(" Program Arguments: [image_name -- default ../data/lena.jpg] \n");
   return -1;
   }
   // Reduce noise by blurring with a Gaussian filter ( kernel size = 3 )
   GaussianBlur( src, src, Size(3, 3), 0, 0, BORDER_DEFAULT );
   cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to grayscale
   Mat abs_dst;
   Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
   // converting back to CV_8U
   convertScaleAbs( dst, abs_dst );
   imshow( window_name, abs_dst );
   waitKey(0);
   return 0;
   }

Java

1. The tutorial code's is shown lines below. You can also download it from here
   import org.opencv.core.*;
   import org.opencv.highgui.HighGui;
   import org.opencv.imgcodecs.Imgcodecs;
   import org.opencv.imgproc.Imgproc;
   class LaplaceDemoRun {
   public void run(String[] args) {
   // Declare the variables we are going to use
   Mat src, src_gray = new Mat(), dst = new Mat();
   int kernel_size = 3;
   int scale = 1;
   int delta = 0;
   int ddepth = CvType.CV_16S;
   String window_name = "Laplace Demo";
   String imageName = ((args.length > 0) ? args[0] : "../data/lena.jpg");
   src = Imgcodecs.imread(imageName, Imgcodecs.IMREAD_COLOR); // Load an image
   // Check if image is loaded fine
   if( src.empty() ) {
   System.out.println("Error opening image");
   System.out.println("Program Arguments: [image_name -- default ../data/lena.jpg] \n");
   System.exit(-1);
   }
   // Reduce noise by blurring with a Gaussian filter ( kernel size = 3 )
   Imgproc.GaussianBlur( src, src, new Size(3, 3), 0, 0, Core.BORDER_DEFAULT );
   // Convert the image to grayscale
   Imgproc.cvtColor( src, src_gray, Imgproc.COLOR_RGB2GRAY );
   Mat abs_dst = new Mat();
   Imgproc.Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, Core.BORDER_DEFAULT );
   // converting back to CV_8U
   Core.convertScaleAbs( dst, abs_dst );
   HighGui.imshow( window_name, abs_dst );
   HighGui.waitKey(0);
   System.exit(0);
   }
   }
   public class LaplaceDemo {
   public static void main(String[] args) {
   // Load the native library.
   System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
   new LaplaceDemoRun().run(args);
   }
   }

Python

1. The tutorial code's is shown lines below. You can also download it from here
   """
   @file laplace_demo.py
   @brief Sample code showing how to detect edges using the Laplace operator
   """
   import sys
   import cv2 as cv
   def main(argv):
   # [variables]
   # Declare the variables we are going to use
   ddepth = cv.CV_16S
   kernel_size = 3
   window_name = "Laplace Demo"
   # [variables]
   # [load]
   imageName = argv[0] if len(argv) > 0 else "../data/lena.jpg"
   src = cv.imread(imageName, cv.IMREAD_COLOR) # Load an image
   # Check if image is loaded fine
   if src is None:
   print ('Error opening image')
   print ('Program Arguments: [image_name -- default ../data/lena.jpg]')
   return -1
   # [load]
   # [reduce_noise]
   # Remove noise by blurring with a Gaussian filter
   src = cv.GaussianBlur(src, (3, 3), 0)
   # [reduce_noise]
   # [convert_to_gray]
   # Convert the image to grayscale
   src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
   # [convert_to_gray]
   # Create Window
   cv.namedWindow(window_name, cv.WINDOW_AUTOSIZE)
   # [laplacian]
   # Apply Laplace function
   dst = cv.Laplacian(src_gray, ddepth, kernel_size)
   # [laplacian]
   # [convert]
   # converting back to uint8
   abs_dst = cv.convertScaleAbs(dst)
   # [convert]
   # [display]
   cv.imshow(window_name, abs_dst)
   cv.waitKey(0)
   # [display]
   return 0
   if __name__ == "__main__":
   main(sys.argv[1:])

## Explanation

#### Declare variables

C++
// Declare the variables we are going to use
Mat src, src_gray, dst;
int kernel_size = 3;
int scale = 1;
int delta = 0;
int ddepth = CV_16S;
const char* window_name = "Laplace Demo";
Java
// Declare the variables we are going to use
Mat src, src_gray = new Mat(), dst = new Mat();
int kernel_size = 3;
int scale = 1;
int delta = 0;
int ddepth = CvType.CV_16S;
String window_name = "Laplace Demo";
Python
# Declare the variables we are going to use
ddepth = cv.CV_16S
kernel_size = 3
window_name = "Laplace Demo"

#### Load source image

C++
const char* imageName = argc >=2 ? argv[1] : "../data/lena.jpg";
src = imread( imageName, IMREAD_COLOR ); // Load an image
// Check if image is loaded fine
if(src.empty()){
printf(" Error opening image\n");
printf(" Program Arguments: [image_name -- default ../data/lena.jpg] \n");
return -1;
}
Java
String imageName = ((args.length > 0) ? args[0] : "../data/lena.jpg");
src = Imgcodecs.imread(imageName, Imgcodecs.IMREAD_COLOR); // Load an image
// Check if image is loaded fine
if( src.empty() ) {
System.out.println("Error opening image");
System.out.println("Program Arguments: [image_name -- default ../data/lena.jpg] \n");
System.exit(-1);
}
Python
imageName = argv[0] if len(argv) > 0 else "../data/lena.jpg"
src = cv.imread(imageName, cv.IMREAD_COLOR) # Load an image
# Check if image is loaded fine
if src is None:
print ('Error opening image')
print ('Program Arguments: [image_name -- default ../data/lena.jpg]')
return -1

#### Reduce noise

C++
// Reduce noise by blurring with a Gaussian filter ( kernel size = 3 )
GaussianBlur( src, src, Size(3, 3), 0, 0, BORDER_DEFAULT );
Java
// Reduce noise by blurring with a Gaussian filter ( kernel size = 3 )
Imgproc.GaussianBlur( src, src, new Size(3, 3), 0, 0, Core.BORDER_DEFAULT );
Python
# Remove noise by blurring with a Gaussian filter
src = cv.GaussianBlur(src, (3, 3), 0)

#### Grayscale

C++
cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to grayscale
Java
// Convert the image to grayscale
Imgproc.cvtColor( src, src_gray, Imgproc.COLOR_RGB2GRAY );
Python
# Convert the image to grayscale
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

#### Laplacian operator

C++
Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
Java
Imgproc.Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, Core.BORDER_DEFAULT );
Python
# Apply Laplace function
dst = cv.Laplacian(src_gray, ddepth, kernel_size)

- The arguments are:
  - src_gray: The input image.
  - dst: Destination (output) image
  - ddepth: Depth of the destination image. Since our input is CV_8U we define ddepth = CV_16S to avoid overflow
  - kernel_size: The kernel size of the Sobel operator to be applied internally. We use 3 in this example.
  - scale, delta and BORDER_DEFAULT: We leave them as default values.

#### Convert output to a CV_8U image

C++
// converting back to CV_8U
convertScaleAbs( dst, abs_dst );
Java
// converting back to CV_8U
Core.convertScaleAbs( dst, abs_dst );
Python
# converting back to uint8
abs_dst = cv.convertScaleAbs(dst)

#### Display the result

C++
imshow( window_name, abs_dst );
waitKey(0);
Java
HighGui.imshow( window_name, abs_dst );
HighGui.waitKey(0);
Python
cv.imshow(window_name, abs_dst)
cv.waitKey(0)

## Results

1. After compiling the code above, we can run it giving as argument the path to an image. For example, using as an input:

2. We obtain the following result. Notice how the trees and the silhouette of the cow are approximately well defined (except in areas in which the intensity are very similar, i.e. around the cow's head). Also, note that the roof of the house behind the trees (right side) is notoriously marked. This is due to the fact that the contrast is higher in that region.