OpenCV4.9关于矩阵上的掩码操作

2024-03-26 15:52
文章标签 操作 矩阵 掩码 opencv4.9

本文主要是介绍OpenCV4.9关于矩阵上的掩码操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 返回:OpenCV系列文章目录(持续更新中......)

上一篇:如何使用OpenCV扫描图像、查找表和时间测量

下一篇:OpenCV4.9的是如何进行图像操作 

引言:

矩阵上的掩码操作非常简单。这个想法是,我们根据掩码矩阵(也称为内核)重新计算图像中每个像素的值。此蒙版包含的值将调整相邻像素(和当前像素)对新像素值的影响程度。从数学的角度来看,我们用指定的值做一个加权平均值。

测试用例

考虑图像对比度增强方法的问题。对图像的每个像素基本上应用以下公式:

I(i,j)=5∗I(i,j)−[I(i−1,j)+I(i+1,j)+I(i,j−1)+I(i,j+1)]

⟺I(i,j)∗M,where M=i∖j−10+1−10−100−15−1+10−10

第一种表示法是使用公式,而第二种表示法是第一种表示法的压缩版本,使用蒙版。使用蒙版的方法是将蒙版矩阵的中心(以大写字母表示,由零零索引表示)放在要计算的像素上,并将像素值乘以重叠的矩阵值相加。这是一回事,但是在大型矩阵的情况下,后一种符号更容易查看。

代码

C++

您可以从此处下载此源代码,或查看 OpenCV 源代码库示例目录  samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp.

​#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
static void help(char* progName)
{
cout << endl
<< "This program shows how to filter images with mask: the write it yourself and the"
<< "filter2d way. " << endl
<< "Usage:" << endl
<< progName << " [image_path -- default lena.jpg] [G -- grayscale] " << endl << endl;
}
void Sharpen(const Mat& myImage,Mat& Result);
int main( int argc, char* argv[])
{
help(argv[0]);
const char* filename = argc >=2 ? argv[1] : "lena.jpg";
Mat src, dst0, dst1;
if (argc >= 3 && !strcmp("G", argv[2]))
src = imread( samples::findFile( filename ), IMREAD_GRAYSCALE);
else
src = imread( samples::findFile( filename ), IMREAD_COLOR);
if (src.empty())
{
cerr << "Can't open image [" << filename << "]" << endl;
return EXIT_FAILURE;
}
namedWindow("Input", WINDOW_AUTOSIZE);
namedWindow("Output", WINDOW_AUTOSIZE);
imshow( "Input", src );
double t = (double)getTickCount();
Sharpen( src, dst0 );
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Hand written function time passed in seconds: " << t << endl;
imshow( "Output", dst0 );
waitKey();
Mat kernel = (Mat_<char>(3,3) << 0, -1, 0,
-1, 5, -1,
0, -1, 0);
t = (double)getTickCount();
filter2D( src, dst1, src.depth(), kernel );
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Built-in filter2D time passed in seconds: " << t << endl;
imshow( "Output", dst1 );
waitKey();
return EXIT_SUCCESS;
}
void Sharpen(const Mat& myImage,Mat& Result)
{
CV_Assert(myImage.depth() == CV_8U); // accept only uchar images
const int nChannels = myImage.channels();
Result.create(myImage.size(),myImage.type());
for(int j = 1 ; j < myImage.rows-1; ++j)
{
const uchar* previous = myImage.ptr<uchar>(j - 1);
const uchar* current = myImage.ptr<uchar>(j );
const uchar* next = myImage.ptr<uchar>(j + 1);
uchar* output = Result.ptr<uchar>(j);
for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i)
{
output[i] = saturate_cast<uchar>(5*current[i]
-current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);
}
}
Result.row(0).setTo(Scalar(0));
Result.row(Result.rows-1).setTo(Scalar(0));
Result.col(0).setTo(Scalar(0));
Result.col(Result.cols-1).setTo(Scalar(0));
}

Java代码:samples/java/tutorial_code/core/mat_mask_operations/MatMaskOperations.java.

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
class MatMaskOperationsRun {public void run(String[] args) {String filename = "../data/lena.jpg";int img_codec = Imgcodecs.IMREAD_COLOR;if (args.length != 0) {filename = args[0];if (args.length >= 2 && args[1].equals("G"))img_codec = Imgcodecs.IMREAD_GRAYSCALE;}Mat src = Imgcodecs.imread(filename, img_codec);if (src.empty()) {System.out.println("Can't open image [" + filename + "]");System.out.println("Program Arguments: [image_path -- default ../data/lena.jpg] [G -- grayscale]");System.exit(-1);}HighGui.namedWindow("Input", HighGui.WINDOW_AUTOSIZE);HighGui.namedWindow("Output", HighGui.WINDOW_AUTOSIZE);HighGui.imshow( "Input", src );double t = System.currentTimeMillis();Mat dst0 = sharpen(src, new Mat());t = ((double) System.currentTimeMillis() - t) / 1000;System.out.println("Hand written function time passed in seconds: " + t);HighGui.imshow( "Output", dst0 );HighGui.moveWindow("Output", 400, 400);HighGui.waitKey();Mat kern = new Mat(3, 3, CvType.CV_8S);int row = 0, col = 0;kern.put(row, col, 0, -1, 0, -1, 5, -1, 0, -1, 0);t = System.currentTimeMillis();Mat dst1 = new Mat();Imgproc.filter2D(src, dst1, src.depth(), kern);t = ((double) System.currentTimeMillis() - t) / 1000;System.out.println("Built-in filter2D time passed in seconds: " + t);HighGui.imshow( "Output", dst1 );HighGui.waitKey();System.exit(0);}public static double saturate(double x) {return x > 255.0 ? 255.0 : (x < 0.0 ? 0.0 : x);}public Mat sharpen(Mat myImage, Mat Result) {myImage.convertTo(myImage, CvType.CV_8U);int nChannels = myImage.channels();Result.create(myImage.size(), myImage.type());for (int j = 1; j < myImage.rows() - 1; ++j) {for (int i = 1; i < myImage.cols() - 1; ++i) {double sum[] = new double[nChannels];for (int k = 0; k < nChannels; ++k) {double top = -myImage.get(j - 1, i)[k];double bottom = -myImage.get(j + 1, i)[k];double center = (5 * myImage.get(j, i)[k]);double left = -myImage.get(j, i - 1)[k];double right = -myImage.get(j, i + 1)[k];sum[k] = saturate(top + bottom + center + left + right);}Result.put(j, i, sum);}}Result.row(0).setTo(new Scalar(0));Result.row(Result.rows() - 1).setTo(new Scalar(0));Result.col(0).setTo(new Scalar(0));Result.col(Result.cols() - 1).setTo(new Scalar(0));return Result;}
}
public class MatMaskOperations {public static void main(String[] args) {// Load the native library.System.loadLibrary(Core.NATIVE_LIBRARY_NAME);new MatMaskOperationsRun().run(args);}
}

Python代码:

samples/python/tutorial_code/core/mat_mask_operations/mat_mask_operations.py.

from __future__ import print_function
import sys
import time
import numpy as np
import cv2 as cv
def is_grayscale(my_image):return len(my_image.shape) < 3
def saturated(sum_value):if sum_value > 255:sum_value = 255if sum_value < 0:sum_value = 0return sum_value
def sharpen(my_image):if is_grayscale(my_image):height, width = my_image.shapeelse:my_image = cv.cvtColor(my_image, cv.CV_8U)height, width, n_channels = my_image.shaperesult = np.zeros(my_image.shape, my_image.dtype) for j in range(1, height - 1):for i in range(1, width - 1):if is_grayscale(my_image):sum_value = 5 * my_image[j, i] - my_image[j + 1, i] - my_image[j - 1, i] \- my_image[j, i + 1] - my_image[j, i - 1]result[j, i] = saturated(sum_value)else:for k in range(0, n_channels):sum_value = 5 * my_image[j, i, k] - my_image[j + 1, i, k] \- my_image[j - 1, i, k] - my_image[j, i + 1, k]\- my_image[j, i - 1, k]result[j, i, k] = saturated(sum_value) return result
def main(argv):filename = 'lena.jpg'img_codec = cv.IMREAD_COLORif argv:filename = sys.argv[1]if len(argv) >= 2 and sys.argv[2] == "G":img_codec = cv.IMREAD_GRAYSCALEsrc = cv.imread(cv.samples.findFile(filename), img_codec)if src is None:print("Can't open image [" + filename + "]")print("Usage:")print("mat_mask_operations.py [image_path -- default lena.jpg] [G -- grayscale]")return -1cv.namedWindow("Input", cv.WINDOW_AUTOSIZE)cv.namedWindow("Output", cv.WINDOW_AUTOSIZE)cv.imshow("Input", src)t = round(time.time())dst0 = sharpen(src)t = (time.time() - t)print("Hand written function time passed in seconds: %s" % t)cv.imshow("Output", dst0)cv.waitKey()t = time.time() kernel = np.array([[0, -1, 0],[-1, 5, -1],[0, -1, 0]], np.float32) # kernel should be floating point type dst1 = cv.filter2D(src, -1, kernel)# ddepth = -1, means destination image has depth same as input image t = (time.time() - t)print("Built-in filter2D time passed in seconds: %s" % t)cv.imshow("Output", dst1)cv.waitKey(0)cv.destroyAllWindows()return 0
if __name__ == "__main__":main(sys.argv[1:])

基本方法

现在让我们看看如何通过使用基本的像素访问方法或使用 filter2D() 函数来实现这一点。

下面是一个函数,可以执行此操作:

C++代码;

def is_grayscale(my_image):return len(my_image.shape) < 3
def saturated(sum_value):if sum_value > 255:sum_value = 255if sum_value < 0:sum_value = 0return sum_value
def sharpen(my_image):if is_grayscale(my_image):height, width = my_image.shapeelse:my_image = cv.cvtColor(my_image, cv.CV_8U)height, width, n_channels = my_image.shaperesult = np.zeros(my_image.shape, my_image.dtype) for j in range(1, height - 1):for i in range(1, width - 1):if is_grayscale(my_image):sum_value = 5 * my_image[j, i] - my_image[j + 1, i] - my_image[j - 1, i] \- my_image[j, i + 1] - my_image[j, i - 1]result[j, i] = saturated(sum_value)else:for k in range(0, n_channels):sum_value = 5 * my_image[j, i, k] - my_image[j + 1, i, k] \- my_image[j - 1, i, k] - my_image[j, i + 1, k]\- my_image[j, i - 1, k]result[j, i, k] = saturated(sum_value) return result

Java代码:

 public static double saturate(double x) {return x > 255.0 ? 255.0 : (x < 0.0 ? 0.0 : x);}public Mat sharpen(Mat myImage, Mat Result) {myImage.convertTo(myImage, CvType.CV_8U);int nChannels = myImage.channels();Result.create(myImage.size(), myImage.type());for (int j = 1; j < myImage.rows() - 1; ++j) {for (int i = 1; i < myImage.cols() - 1; ++i) {double sum[] = new double[nChannels];for (int k = 0; k < nChannels; ++k) {double top = -myImage.get(j - 1, i)[k];double bottom = -myImage.get(j + 1, i)[k];double center = (5 * myImage.get(j, i)[k]);double left = -myImage.get(j, i - 1)[k];double right = -myImage.get(j, i + 1)[k];sum[k] = saturate(top + bottom + center + left + right);}Result.put(j, i, sum);}}Result.row(0).setTo(new Scalar(0));Result.row(Result.rows() - 1).setTo(new Scalar(0));Result.col(0).setTo(new Scalar(0));Result.col(Result.cols() - 1).setTo(new Scalar(0));return Result;}

Python代码

def is_grayscale(my_image):return len(my_image.shape) < 3
def saturated(sum_value):if sum_value > 255:sum_value = 255if sum_value < 0:sum_value = 0return sum_value
def sharpen(my_image):if is_grayscale(my_image):height, width = my_image.shapeelse:my_image = cv.cvtColor(my_image, cv.CV_8U)height, width, n_channels = my_image.shaperesult = np.zeros(my_image.shape, my_image.dtype) for j in range(1, height - 1):for i in range(1, width - 1):if is_grayscale(my_image):sum_value = 5 * my_image[j, i] - my_image[j + 1, i] - my_image[j - 1, i] \- my_image[j, i + 1] - my_image[j, i - 1]result[j, i] = saturated(sum_value)else:for k in range(0, n_channels):sum_value = 5 * my_image[j, i, k] - my_image[j + 1, i, k] \- my_image[j - 1, i, k] - my_image[j, i + 1, k]\- my_image[j, i - 1, k]result[j, i, k] = saturated(sum_value) return result

首先,我们确保输入图像数据采用无符号字符格式。为此,我们使用cv::CV_Assert 函数,当其中的表达式为 false 时,该函数会抛出错误。

 CV_Assert(myImage.depth() == CV_8U); // 只接受 uchar 图片

我们创建一个与输入具有相同大小和类型的输出图像。正如您在存储部分中看到的那样,根据通道的数量,我们可能有一个或多个子列。

我们将通过指针遍历它们,因此元素的总数取决于这个数字。

const int nChannels = myImage.channels();
Result.create(myImage.size(),myImage.type());

我们将使用普通的 C [] 运算符来访问像素。因为我们需要同时访问多行,所以我们将获取每行的指针(上一行、当前行和下一行)。我们需要另一个指向要保存计算位置的指针。然后,只需使用 [] 运算符访问正确的项目即可。为了将输出指针向前移动,我们只需在每次操作后增加此值(一个字节):

 for(int j = 1 ; j < myImage.rows-1; ++j){const uchar* previous = myImage.ptr<uchar>(j - 1);const uchar* current = myImage.ptr<uchar>(j );const uchar* next = myImage.ptr<uchar>(j + 1);uchar* output = Result.ptr<uchar>(j);for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i){output[i] = saturate_cast<uchar>(5*current[i]-current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);}}

在图像的边框上,上面的符号会导致不存在的像素位置(如负一 - 负一)。在这些方面,我们的公式是未定义的。一个简单的解决方案是不在这些点上应用内核,例如,将边框上的像素设置为零:

 Result.row(0).setTo(Scalar(0));Result.row(Result.rows-1).setTo(Scalar(0));Result.col(0).setTo(Scalar(0));Result.col(Result.cols-1).setTo(Scalar(0));

filter2D 函数
 

应用这种过滤器在图像处理中非常普遍,以至于在 OpenCV 中有一个函数可以负责应用掩码(在某些地方也称为内核)。为此,您首先需要定义一个包含掩码的对象:

C++代码:

 Mat kernel = (Mat_<char>(3,3) << 0, -1, 0,-1, 5, -1,0, -1, 0);

 然后调用 filter2D() 函数,指定要使用的输入、输出图像和内核:

 filter2D( src, dst1, src.depth(), kernel );

 Java代码:

 Mat kern = new Mat(3, 3, CvType.CV_8S);int row = 0, col = 0;kern.put(row, col, 0, -1, 0, -1, 5, -1, 0, -1, 0);

 然后调用 filter2D() 函数,指定要使用的输入、输出图像和内核:

 Imgproc.filter2D(src, dst1, src.depth(), kern);

Python代码:

 kernel = np.array([[0, -1, 0],[-1, 5, -1],[0, -1, 0]], np.float32) # kernel should be floating point type

 然后调用 filter2D() 函数,指定要使用的输入、输出图像和内核: 

 dst1 = cv.filter2D(src, -1, kernel)# ddepth = -1, means destination image has depth same as input image

该函数甚至还有第五个可选参数来指定内核的中心,第六个参数用于在将过滤后的像素存储在 K 中之前向它们添加可选值,第七个参数用于确定在未定义操作的区域(边界)中执行的操作。

此函数更短,更不冗长,并且由于进行了一些优化,因此通常比手动编码方法更快。例如,在我的测试中,第二个测试只用了 13 毫秒,而第一个测试大约需要 31 毫秒。相当有区别。

例如:

resultMatMaskFilter2D.png

在我们的 YouTube 频道上查看运行该程序的实例。

参考文章:

1、《Mask operations on matrices》-----Bernát Gábor

这篇关于OpenCV4.9关于矩阵上的掩码操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/849027

相关文章

hdu 4565 推倒公式+矩阵快速幂

题意 求下式的值: Sn=⌈ (a+b√)n⌉%m S_n = \lceil\ (a + \sqrt{b}) ^ n \rceil\% m 其中: 0<a,m<215 0< a, m < 2^{15} 0<b,n<231 0 < b, n < 2^{31} (a−1)2<b<a2 (a-1)^2< b < a^2 解析 令: An=(a+b√)n A_n = (a +

hdu 6198 dfs枚举找规律+矩阵乘法

number number number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description We define a sequence  F : ⋅   F0=0,F1=1 ; ⋅   Fn=Fn

动手学深度学习【数据操作+数据预处理】

import osos.makedirs(os.path.join('.', 'data'), exist_ok=True)data_file = os.path.join('.', 'data', 'house_tiny.csv')with open(data_file, 'w') as f:f.write('NumRooms,Alley,Price\n') # 列名f.write('NA

线程的四种操作

所属专栏:Java学习        1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链表中),此处的start会根据不同的系统,分别调用不同的api,创建好之后的线程,再单独去执行run(所以说,start的本质是调用系统api,系统的api

Java IO 操作——个人理解

之前一直Java的IO操作一知半解。今天看到一个便文章觉得很有道理( 原文章),记录一下。 首先,理解Java的IO操作到底操作的什么内容,过程又是怎么样子。          数据来源的操作: 来源有文件,网络数据。使用File类和Sockets等。这里操作的是数据本身,1,0结构。    File file = new File("path");   字

MySQL——表操作

目录 一、创建表 二、查看表 2.1 查看表中某成员的数据 2.2 查看整个表中的表成员 2.3 查看创建表时的句柄 三、修改表 alter 3.1 重命名 rename 3.2 新增一列 add 3.3 更改列属性 modify 3.4 更改列名称 change 3.5 删除某列 上一篇博客介绍了库的操作,接下来来看一下表的相关操作。 一、创建表 create

封装MySQL操作时Where条件语句的组织

在对数据库进行封装的过程中,条件语句应该是相对难以处理的,毕竟条件语句太过于多样性。 条件语句大致分为以下几种: 1、单一条件,比如:where id = 1; 2、多个条件,相互间关系统一。比如:where id > 10 and age > 20 and score < 60; 3、多个条件,相互间关系不统一。比如:where (id > 10 OR age > 20) AND sco

PHP7扩展开发之流操作

前言 啥是流操作?简单来讲就是对一些文件,网络的IO操作。PHP已经把这些IO操作,封装成流操作。这节,我们将使用PHP扩展实现一个目录遍历的功能。PHP示例代码如下: <?phpfunction list_dir($dir) {if (is_dir($dir) === false) {return;} $dh = opendir($dir);if ($dh == false) {ret

浙大数据结构:树的定义与操作

四种遍历 #include<iostream>#include<queue>using namespace std;typedef struct treenode *BinTree;typedef BinTree position;typedef int ElementType;struct treenode{ElementType data;BinTree left;BinTre

浙大数据结构:04-树7 二叉搜索树的操作集

这道题答案都在PPT上,所以先学会再写的话并不难。 1、BinTree Insert( BinTree BST, ElementType X ) 递归实现,小就进左子树,大就进右子树。 为空就新建结点插入。 BinTree Insert( BinTree BST, ElementType X ){if(!BST){BST=(BinTree)malloc(sizeof(struct TNo