1.在OpenCV中我们经常会遇到一个名字:Mask(掩膜)。很多函数都使用到它,那么这个Mask到底什么呢?
2.如果我们想要裁剪图像中任意形状的区域时,应该怎么办呢?
答案是,使用掩膜(masking)。
我们先看一下掩膜的基础。图像的位运算。
上述的基本操作中都属于将基础数学运算应用于图像像素的处理中,下面将着重介绍
为了便于大家进一步理解,下面给出测试代码:
# opencv 图像的基本运算
# 导入库
import numpy as np
import argparse
import cv2
# 构建参数解析器
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Path to the image")
args = vars(ap.parse_args())
# 加载图像
image = cv2.imread(args["image"])
cv2.imshow("image loaded", image)
# 创建矩形区域,填充白色255
rectangle = np.zeros(image.shape[0:2], dtype="uint8")
cv2.rectangle(rectangle, (25, 25), (275, 275), 255, -1) # 修改这里
cv2.imshow("Rectangle", rectangle)
# 创建圆形区域,填充白色255
circle = np.zeros(image.shape[0:2], dtype="uint8")
cv2.circle(circle, (150, 150), 150, 255, -1) # 修改
cv2.imshow("Circle", circle)
# 在此例(二值图像)中,以下的0表示黑色像素值0, 1表示白色像素值255
# 位与运算,与常识相同,有0则为0, 均无0则为1
bitwiseAnd = cv2.bitwise_and(rectangle, circle)
cv2.imshow("AND", bitwiseAnd)
cv2.waitKey(0)
# 非运算,非0为1, 非1为0
bitwiseNot = cv2.bitwise_not(circle)
cv2.imshow("NOT", bitwiseNot)
cv2.waitKey(0)
# 或运算,有1则为1, 全为0则为0
bitwiseOr = cv2.bitwise_or(rectangle, circle)
cv2.imshow("OR", bitwiseOr)
cv2.waitKey(0)
# 异或运算,不同为1, 相同为0
bitwiseXor = cv2.bitwise_xor(rectangle, circle)
cv2.imshow("XOR", bitwiseXor)
cv2.waitKey(0)
可以看到,原图是一张星空夜景图。
效果如下:
为了便于展示,后面我只截取部分区域效果:
如开篇所提问题2,要对一幅图进行抠图(裁剪)操作,这就要用到Mask了,那么就以抠图为例,解释Mask在里面的作用。同样以上图为例,从原图中裁剪小树。
我们利用OR结果(其他结果也行),修改调整后,
代码如下:
# opencv 图像的基本运算
# 导入库
import numpy as np
import argparse
import cv2
# 构建参数解析器
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Path to the image")
args = vars(ap.parse_args())
# 加载图像
image = cv2.imread(args["image"])
cv2.imshow("image loaded", image)
# 创建矩形区域,填充白色255
rectangle = np.zeros(image.shape[0:2], dtype="uint8")
cv2.rectangle(rectangle, (360, 348), (660, 570), 255, -1) # 修改这里
cv2.imshow("Rectangle", rectangle)
# 创建圆形区域,填充白色255
circle = np.zeros(image.shape[0:2], dtype="uint8")
cv2.circle(circle, (520, 455), 140, 255, -1) # 修改
cv2.imshow("Circle", circle)
'''
# 在此例(二值图像)中,以下的0表示黑色像素值0, 1表示白色像素值255
# 位与运算,与常识相同,有0则为0, 均无0则为1
bitwiseAnd = cv2.bitwise_and(rectangle, circle)
cv2.imshow("AND", bitwiseAnd)
cv2.waitKey(0)
# 非运算,非0为1, 非1为0
bitwiseNot = cv2.bitwise_not(circle)
cv2.imshow("NOT", bitwiseNot)
cv2.waitKey(0)
# 异或运算,不同为1, 相同为0
bitwiseXor = cv2.bitwise_xor(rectangle, circle)
cv2.imshow("XOR", bitwiseXor)
cv2.waitKey(0)
'''
# 或运算,有1则为1, 全为0则为0
bitwiseOr = cv2.bitwise_or(rectangle, circle)
cv2.imshow("OR", bitwiseOr)
cv2.waitKey(0)
# 使用mask
mask = bitwiseOr
cv2.imshow("Mask", mask)
# Apply out mask -- notice how only the person in the image is cropped out
masked = cv2.bitwise_and(image, image, mask=mask)
cv2.imshow("Mask Applied to Image", masked)
cv2.waitKey(0)
结果展示:
简单说就是:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image, mask;
Rect r1(100, 100, 250, 300);
Mat img1, img2, img3, img4;
image = imread("lol17.jpg");
mask = Mat::zeros(image.size(), CV_8UC1);
mask(r1).setTo(255);
img1 = image(r1);
image.copyTo(img2, mask);
image.copyTo(img3);
img3.setTo(0, mask);
imshow("Image sequence", image);
imshow("img1", img1);
imshow("img2", img2);
imshow("img3", img3);
imshow("mask", mask);
waitKey();
return 0;
}
原始图:
注意程序中的这两句关于Mask的操作。
mask = Mat::zeros(image.size(), CV_8UC1);
mask(r1).setTo(255); //r1是设置好的感兴趣区域
解释一下上面两句的操作。
注意这句,哪个图像拷贝到哪个图像?
image.copyTo(img2, mask);
当然是原始图image拷贝到目的图img2上。
其实拷贝的动作完整版本是这样的:
原图(image)与掩膜(mask)进行与运算后得到了结果图(img2)。
何为图与掩膜的与运算?
其实就是原图中的每个像素和掩膜中的每个对应像素进行与运算。比如1 & 1 = 1;1 & 0 = 0;
比如一个3 * 3的图像与3 * 3的掩膜进行运算,得到的结果图像就是:
说白了,mask就是位图啊,来选择哪个像素允许拷贝,哪个像素不允许拷贝。如果mask像素的值是非0的,我就拷贝它,否则不拷贝。
因为我们上面得到的mask中,感兴趣的区域是白色的,表明感兴趣区域的像素都是非0,而非感兴趣区域都是黑色,表明那些区域的像素都是0。一旦原图与mask图进行与运算后,得到的结果图只留下原始图感兴趣区域的图像了。也正如下图所示。
image.copyTo(img2, mask);
如果想要直接抠出目标区域,直接这样写就OK了:
img1 = image(r1);
参考文献:
1.https://www.cnblogs.com/skyfsm/p/6894685.html