图像处理:变换——为图像添加边框 OpenCV v4.8.0

上一个教程自制线性滤镜

下一个教程Sobel 边缘检测算子

原作者Ana Huamán
兼容性OpenCV >= 3.0

目标

在本教程中,你将学习如何

  • 使用 OpenCV 函数 copyMakeBorder() 设置边框(图像的额外填充)。

原理

注释
以下解释来自 Bradski 和 Kaehler 合著的《Learning OpenCV》一书。

  1. 在上一教程中,我们学习了使用卷积对图像进行操作。自然而然出现的一个问题是如何处理边界。如果评估点位于图像边缘,我们该如何卷积?

  2. 大多数 OpenCV 函数的做法是将给定图像复制到另一幅稍大的图像上,然后自动填充边界(采用下面示例代码中解释的任何一种方法)。这样,卷积就可以在所需像素上顺利进行(操作完成后,多余的填充会被剪掉)。

  3. 在本教程中,我们将简要探讨为图像定义额外填充(边框)的两种方法:

    a. BORDER_CONSTANT:用一个常量值(如黑色或 0)填充图像。
    b. BORDER_REPLICATE:将原始图像边缘的行或列复制到额外边框中。
    代码部分将更清楚地说明这一点。

  • 这个程序要做什么?
    • 加载图像

    • 让用户选择在输入图像中使用哪种填充。有两个选项

      1. 恒定值边框: 为整个边框应用一个恒定值的填充。该值每 0.5 秒随机更新一次。
      2. 复制边框: 边框将根据原始图像边缘的像素值复制。
        用户可按 “c”(恒定)或 “r”(复制)来选择其中一个选项。
    • 当用户按下 "ESC "键时,程序结束。

代码

C++
教程代码如下所示。

您也可以从此处下载

#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
// 声明变量
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"// 加载图像
 src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // 加载图像
 // 检查图片加载是否正常
 if( src.empty()) {
 printf(" Error opening image\n");
 printf(" Program Arguments: [image_name -- default lena.jpg] \n");
 return -1;
 }
 // 该程序的简要操作方法
 printf( "\n \t copyMakeBorder Demo: \n" );
 printf( "\t -------------------- \n" );
 printf( " ** Press 'c' to set the border to a random constant value \n");
 printf( " ** Press 'r' to set the border to be replicated \n");
 printf( " ** Press 'ESC' to exit the program \n");
 namedWindow( window_name, WINDOW_AUTOSIZE );
 // 初始化过滤器参数
 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
教程代码如下所示。

您也可以从此处下载


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) {
 // 声明变量
 Mat src, dst = new Mat()int top, bottom, left, right;
 int borderType = Core.BORDER_CONSTANTString window_name = "copyMakeBorder Demo"Random rng;
 String imageName = ((args.length > 0) ? args[0] : "../data/lena.jpg")// 加载图像
 src = Imgcodecs.imread(imageName, Imgcodecs.IMREAD_COLOR)// 检查图片加载是否正常
 if( src.empty() ) {
 System.out.println("Error opening image!");
 System.out.println("Program Arguments: [image_name -- default ../data/lena.jpg] \n");
 System.exit(-1);
 }
 // 该程序的简要说明
 System.out.println("\n" +
 "\t copyMakeBorder Demo: \n" +
 "\t -------------------- \n" +
 " ** Press 'c' to set the border to a random constant value \n" +
 " ** Press 'r' to set the border to be replicated \n" +
 " ** Press 'ESC' to exit the program \n");
 HighGui.namedWindow( window_name, HighGui.WINDOW_AUTOSIZE );
 // 初始化过滤器参数
 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) {
 // 加载本地库。
 System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
 new CopyMakeBorderRun().run(args);
 }
}

Python
教程代码如下所示。

您也可以从此处下载

"""
@file copy_make_border.py
@brief Sample code that shows the functionality of copyMakeBorder
"""
import sys
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' # 加载图片
 src = cv.imread(cv.samples.findFile(imageName), cv.IMREAD_COLOR)
 # 检查图片加载是否正常
 if src is None:
 print ('Error opening image!')
 print ('Usage: copy_make_border.py [image_name -- default lena.jpg] \n')
 return -1
 
 print ('\n'
 '\t copyMakeBorder Demo: \n'
 ' -------------------- \n'
 ' ** Press \'c\' to set the border to a random constant value \n'
 ' ** Press \'r\' to set the border to be replicated \n'
 ' ** 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:])

说明

声明变量

首先,我们声明将要使用的变量:
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"

需要特别注意的是变量 rng,它是一个随机数生成器。我们用它来生成随机边框颜色,很快就会看到。

加载图片

像往常一样,我们载入源图像 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\n");
 printf(" Program Arguments: [image_name -- default lena.jpg] \n");
 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] \n");
 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] \n')
 return -1

创建窗口

在简要介绍如何使用程序后,我们创建一个窗口:
C++

 namedWindow( window_name, WINDOW_AUTOSIZE );

Java

 HighGui.namedWindow( window_name, HighGui.WINDOW_AUTOSIZE );

Python

 cv.namedWindow(window_name, cv.WINDOW_AUTOSIZE)

初始化参数

现在我们初始化定义边框大小(顶部、底部、左侧和右侧)的参数。我们赋予它们的值是 src 大小的 5%。
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

循环

如果不按下 ESC 键,程序将无限循环运行。如果用户按下 "c "或 "r "键,则 borderType 变量的值分别为 BORDER_CONSTANTBORDER_REPLICATE
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

随机颜色

每次迭代(0.5 秒后),随机边框颜色(值)都会更新…
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)]

该值是在 [0,255] 范围内随机选取的三个数字的集合。

在图像周围形成边框

最后,我们调用 copyMakeBorder() 函数来应用相应的填充:
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)
  • 参数如下
    1. src:源图像
    2. dst: 目标图像
    3. top, bottom, left, right: 图像两侧边框的长度(以像素为单位)。我们将其定义为图像原始大小的 5%。
    4. borderType:边框类型: 定义应用的边框类型。本示例中可以使用常量或复制。
    5. value: 如果 borderType 为 BORDER_CONSTANT,则此值用于填充边框像素。

显示结果

我们在之前创建的图像中显示输出图像
C++

 imshow( window_name, dst );

Java

 HighGui.imshow( window_name, dst );

Python

 cv.imshow(window_name, dst)

结果

  1. 编译完上面的代码后,可以将图像路径作为参数来执行。结果应该是

    • 默认情况下,开始时边框设置为 BORDER_CONSTANT。因此,将显示一系列随机颜色的边框。
    • 如果按 "r "键,边框将变成边缘像素的复制品。
    • 如果按下 "c "键,将再次出现随机彩色边框。
    • 如果按 "ESC "键,程序将退出。

下面的截图显示了边框的颜色变化和 BORDER_REPLICATE 选项的效果:

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值