Image processing: Transformation – Add borders to images OpenCV v4.8.0

Previous tutorial: Homemade linear filter

Next tutorial: Sobel edge detection operator

Original author Ana Huamán
Compatibility OpenCV >= 3.0

Goals

In this tutorial you will learn how to

  • Use the OpenCV function copyMakeBorder() to set the border (extra padding for the image).

Principles

Comments
The following explanation comes from the book Learning OpenCV by Bradski and Kaehler.

  1. In the previous tutorial, we learned about using convolutions to operate on images. A question that naturally arises is how to deal with boundaries. How do we convolve if the evaluation point is at the edge of the image?

  2. What most OpenCV functions do is copy the given image onto another, slightly larger image, and then automatically fill in the boundaries (using any of the methods explained in the sample code below). This way, the convolution proceeds smoothly on the desired pixels (excess padding is clipped off after the operation is complete).

  3. In this tutorial, we will briefly explore two methods of defining additional padding (border) for images:

    a. BORDER_CONSTANT: Fills the image with a constant value (such as black or 0).
    b. BORDER_REPLICATE: Copy rows or columns from the edge of the original image into an extra border.
    The code section will illustrate this more clearly.

  • What does this program do?
    • Load image

    • Let the user choose which padding to use in the input image. There are two options

      1. Constant value border: Applies a constant value padding to the entire border. The value is updated randomly every 0.5 seconds.
      2. Copy Border: The border will be copied based on the pixel values of the edges of the original image.
        The user can press “c” (constant) or “r” (replicate) to select one of the options.
    • The program ends when the user presses the “ESC” key.

Code

C++
The tutorial code is shown below.

You can also download it from here

#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
// declare variables
Mat src, dst;
int top, bottom, left, right;
int borderType = BORDER_CONSTANT;
const char* window_name = "copyMakeBorder Demo";
RNG rng(12345);
int main(int argc, char** argv)
{<!-- -->
 const char* imageName = argc >=2 ? argv[1] : "lena.jpg";
 //Load image
 src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load image
 // Check whether the image is loaded normally
 if( src.empty()) {<!-- -->
 printf("Error opening image\
");
 printf("Program Arguments: [image_name -- default lena.jpg] \
");
 return -1;
 }
 // Brief operation method of this program
 printf( "\
 \t copyMakeBorder Demo: \
" );
 printf( "\t -------------------- \
" );
 printf( " ** Press 'c' to set the border to a random constant value \
");
 printf( " ** Press 'r' to set the border to be replicated \
");
 printf( " ** Press 'ESC' to exit the program \
");
 namedWindow(window_name, WINDOW_AUTOSIZE);
 //Initialize filter parameters
 top = (int) (0.05*src.rows); bottom = top;
 left = (int) (0.05*src.cols); right = left;
 for(;;)
 {<!-- -->
 Scalar value( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
 copyMakeBorder( src, dst, top, bottom, left, right, borderType, value );
 imshow(window_name, dst);
 char c = (char)waitKey(500);
 if( c == 27 )
 {<!-- --> break; }
 else if( c == 'c' )
 {<!-- --> borderType = BORDER_CONSTANT; }
 else if( c == 'r' )
 {<!-- --> borderType = BORDER_REPLICATE; }
 }
 return 0;
}

Java
The tutorial code is shown below.

You can also download it from here

import org.opencv.core.*;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import java.util.Random;
class CopyMakeBorderRun {<!-- -->
 public void run(String[] args) {<!-- -->
 // declare variables
 Mat src, dst = new Mat();
 int top, bottom, left, right;
 int borderType = Core.BORDER_CONSTANT;
 String window_name = "copyMakeBorder Demo";
 Random rng;
 String imageName = ((args.length > 0) ? args[0] : "../data/lena.jpg");
 //Load image
 src = Imgcodecs.imread(imageName, Imgcodecs.IMREAD_COLOR);
 // Check whether the image is loaded normally
 if( src.empty() ) {<!-- -->
 System.out.println("Error opening image!");
 System.out.println("Program Arguments: [image_name -- default ../data/lena.jpg] \
");
 System.exit(-1);
 }
 // A brief description of the program
 System.out.println("\
" +
 "\t copyMakeBorder Demo: \
" +
 "\t-------------------- \
" +
 " ** Press 'c' to set the border to a random constant value \
" +
 " ** Press 'r' to set the border to be replicated \
" +
 " ** Press 'ESC' to exit the program \
");
 HighGui.namedWindow( window_name, HighGui.WINDOW_AUTOSIZE );
 //Initialize filter parameters
 top = (int) (0.05*src.rows()); bottom = top;
 left = (int) (0.05*src.cols()); right = left;
 while( true ) {<!-- -->
 rng = new Random();
 Scalar value = new Scalar( rng.nextInt(256),
 rng.nextInt(256), rng.nextInt(256) );
 Core.copyMakeBorder(src, dst, top, bottom, left, right, borderType, value);
 HighGui.imshow(window_name, dst);
 char c = (char) HighGui.waitKey(500);
 c = Character.toLowerCase(c);
 if( c == 27 )
 {<!-- --> break; }
 else if( c == 'c' )
 {<!-- --> borderType = Core.BORDER_CONSTANT;}
 else if( c == 'r' )
 {<!-- --> borderType = Core.BORDER_REPLICATE;}
 }
 System.exit(0);
 }
}
public class CopyMakeBorder {<!-- -->
 public static void main(String[] args) {<!-- -->
 //Load the local library.
 System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
 new CopyMakeBorderRun().run(args);
 }
}

Python
The tutorial code is shown below.

You can also download it from here

"""
@file copy_make_border.py
@brief Sample code that shows the functionality of copyMakeBorder
"""
importsys
from random import randint
import cv2 as cv
def main(argv):
 
 borderType = cv.BORDER_CONSTANT
 window_name = "copyMakeBorder Demo"
 
 imageName = argv[0] if len(argv) > 0 else 'lena.jpg' #Load image
 src = cv.imread(cv.samples.findFile(imageName), cv.IMREAD_COLOR)
 # Check whether the image is loaded normally
 if src is None:
 print ('Error opening image!')
 print ('Usage: copy_make_border.py [image_name -- default lena.jpg] \
')
 return -1
 
 print ('\
'
 '\t copyMakeBorder Demo: \
'
 '--------------------\
'
 ' ** Press 'c' to set the border to a random constant value \
'
 ' ** Press 'r' to set the border to be replicated \
'
 ' ** Press 'ESC' to exit the program ')
 
 cv.namedWindow(window_name, cv.WINDOW_AUTOSIZE)
 
 top = int(0.05 * src.shape[0]) # shape[0] = rows
 bottom = top
 left = int(0.05 * src.shape[1]) # shape[1] = cols
 right = left
 
 while 1:
 
 value = [randint(0, 255), randint(0, 255), randint(0, 255)]
 
 dst = cv.copyMakeBorder(src, top, bottom, left, right, borderType, None, value)
 
 cv.imshow(window_name, dst)
 
 c = cv.waitKey(500)
 if c == 27:
 break
 elif c == 99: # 99 = ord('c')
 borderType = cv.BORDER_CONSTANT
 elif c == 114: # 114 = ord('r')
 borderType = cv.BORDER_REPLICATE
 
 return 0
if __name__ == "__main__":
 main(sys.argv[1:])

Description

Declare variables

First, we declare the variables we will use:
C++

// Declare the variables
Mat src, dst;
int top, bottom, left, right;
int borderType = BORDER_CONSTANT;
const char* window_name = "copyMakeBorder Demo";
RNG rng(12345);

Java

 // Declare the variables
 Mat src, dst = new Mat();
 int top, bottom, left, right;
 int borderType = Core.BORDER_CONSTANT;
 String window_name = "copyMakeBorder Demo";
 Random rng;

Python

 # First we declare the variables we are going to use
 borderType = cv.BORDER_CONSTANT
 window_name = "copyMakeBorder Demo"

Of particular note is the variable rng, which is a random number generator. We use this to generate random border colors, as you’ll see shortly.

Load images

As usual, we load the source image src:
C++

 const char* imageName = argc >=2 ? argv[1] : "lena.jpg";
 // Loads an image
 src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
 // Check if image is loaded fine
 if( src.empty()) {<!-- -->
 printf("Error opening image\
");
 printf("Program Arguments: [image_name -- default lena.jpg] \
");
 return -1;
 }

Java

 String imageName = ((args.length > 0) ? args[0] : "../data/lena.jpg");
 // Load an image
 src = Imgcodecs.imread(imageName, Imgcodecs.IMREAD_COLOR);
 // 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] \
");
 System.exit(-1);
 }

Python

 imageName = argv[0] if len(argv) > 0 else 'lena.jpg'
 #Loads an image
 src = cv.imread(cv.samples.findFile(imageName), cv.IMREAD_COLOR)
 # Check if image is loaded fine
 if src is None:
 print ('Error opening image!')
 print ('Usage: copy_make_border.py [image_name -- default lena.jpg] \
')
 return -1

Create window

After a brief introduction on how to use the program, we create a window:
C++

 namedWindow( window_name, WINDOW_AUTOSIZE );

Java

 HighGui.namedWindow( window_name, HighGui.WINDOW_AUTOSIZE );

Python

 cv.namedWindow(window_name, cv.WINDOW_AUTOSIZE)

Initialization parameters

Now we initialize the parameters that define the border size (top, bottom, left and right). The value we give them is 5% of the size of src.
C++

 // Initialize arguments for the filter
 top = (int) (0.05*src.rows); bottom = top;
 left = (int) (0.05*src.cols); right = left;

Java

 // Initialize arguments for the filter
 top = (int) (0.05*src.rows()); bottom = top;
 left = (int) (0.05*src.cols()); right = left;

Python

 # Initialize arguments for the filter
 top = int(0.05 * src.shape[0]) # shape[0] = rows
 bottom = top
 left = int(0.05 * src.shape[1]) # shape[1] = cols
 right = left

Loop

If the ESC key is not pressed, the program will run in an infinite loop. If the user presses the “c” or “r” key, the value of the borderType variable is BORDER_CONSTANT or BORDER_REPLICATE respectively:
C++

 char c = (char)waitKey(500);
 if( c == 27 )
 {<!-- --> break; }
 else if( c == 'c' )
 {<!-- --> borderType = BORDER_CONSTANT; }
 else if( c == 'r' )
 {<!-- --> borderType = BORDER_REPLICATE; }

Java

 char c = (char) HighGui.waitKey(500);
 c = Character.toLowerCase(c);
 if( c == 27 )
 {<!-- --> break; }
 else if( c == 'c' )
 {<!-- --> borderType = Core.BORDER_CONSTANT;}
 else if( c == 'r' )
 {<!-- --> borderType = Core.BORDER_REPLICATE;}

Python

 c = cv.waitKey(500)
 if c == 27:
 break
 elif c == 99: # 99 = ord('c')
 borderType = cv.BORDER_CONSTANT
 elif c == 114: # 114 = ord('r')
 borderType = cv.BORDER_REPLICATE

Random Color

Every iteration (after 0.5 seconds) the random border color (value) is updated…
C++

 Scalar value( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );

Java

 rng = new Random();
 Scalar value = new Scalar( rng.nextInt(256),
 rng.nextInt(256), rng.nextInt(256) );

Python

 value = [randint(0, 255), randint(0, 255), randint(0, 255)]

The value is a set of three randomly chosen numbers in the range [0,255].

Form a border around the image

Finally, we call the copyMakeBorder() function to apply the appropriate padding:
C++

 copyMakeBorder( src, dst, top, bottom, left, right, borderType, value );

Java

 Core.copyMakeBorder(src, dst, top, bottom, left, right, borderType, value);

Python

 dst = cv.copyMakeBorder(src, top, bottom, left, right, borderType, None, value)
  • The parameters are as follows
    1. src: source image
    2. dst: target image
    3. top, bottom, left, right: The length of the borders on both sides of the image (in pixels). We define this as 5% of the original size of the image.
    4. borderType: Border type: Defines the applied border type. Constants or copying can be used in this example.
    5. value: If borderType is BORDER_CONSTANT, this value is used to fill the border pixels.

Show results

We display the output image in the image we created earlier
C++

 imshow(window_name, dst);

Java

 HighGui.imshow( window_name, dst );

Python

 cv.imshow(window_name, dst)

Results

  1. After compiling the above code, it can be executed with the image path as a parameter. The result should be

    • By default, the border is set to BORDER_CONSTANT initially. Therefore, a range of randomly colored borders will be displayed.
    • If you press the “r” key, the border will become a copy of the edge pixels.
    • If you press the “c” key, a random colored border will appear again.
    • If you press the “ESC” key, the program will exit.

The screenshot below shows the color change of the border and the effect of the BORDER_REPLICATE option: