上一个教程 : 自制线性滤镜
下一个教程 : Sobel 边缘检测算子
原作者 | Ana Huamán |
---|---|
兼容性 | OpenCV >= 3.0 |
目标
在本教程中,你将学习如何
- 使用 OpenCV 函数 copyMakeBorder() 设置边框(图像的额外填充)。
原理
注释
以下解释来自 Bradski 和 Kaehler 合著的《Learning OpenCV》一书。
-
在上一教程中,我们学习了使用卷积对图像进行操作。自然而然出现的一个问题是如何处理边界。如果评估点位于图像边缘,我们该如何卷积?
-
大多数 OpenCV 函数的做法是将给定图像复制到另一幅稍大的图像上,然后自动填充边界(采用下面示例代码中解释的任何一种方法)。这样,卷积就可以在所需像素上顺利进行(操作完成后,多余的填充会被剪掉)。
-
在本教程中,我们将简要探讨为图像定义额外填充(边框)的两种方法:
a. BORDER_CONSTANT:用一个常量值(如黑色或 0)填充图像。
b. BORDER_REPLICATE:将原始图像边缘的行或列复制到额外边框中。
代码部分将更清楚地说明这一点。
- 这个程序要做什么?
-
加载图像
-
让用户选择在输入图像中使用哪种填充。有两个选项
- 恒定值边框: 为整个边框应用一个恒定值的填充。该值每 0.5 秒随机更新一次。
- 复制边框: 边框将根据原始图像边缘的像素值复制。
用户可按 “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_CONSTANT;
String 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_CONSTANT 或 BORDER_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)
- 参数如下
- src:源图像
- dst: 目标图像
- top, bottom, left, right: 图像两侧边框的长度(以像素为单位)。我们将其定义为图像原始大小的 5%。
- borderType:边框类型: 定义应用的边框类型。本示例中可以使用常量或复制。
- value: 如果 borderType 为 BORDER_CONSTANT,则此值用于填充边框像素。
显示结果
我们在之前创建的图像中显示输出图像
C++
imshow( window_name, dst );
Java
HighGui.imshow( window_name, dst );
Python
cv.imshow(window_name, dst)
结果
-
编译完上面的代码后,可以将图像路径作为参数来执行。结果应该是
- 默认情况下,开始时边框设置为 BORDER_CONSTANT。因此,将显示一系列随机颜色的边框。
- 如果按 "r "键,边框将变成边缘像素的复制品。
- 如果按下 "c "键,将再次出现随机彩色边框。
- 如果按 "ESC "键,程序将退出。
下面的截图显示了边框的颜色变化和 BORDER_REPLICATE 选项的效果: