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

相关文章

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

Python利用自带模块实现屏幕像素高效操作

《Python利用自带模块实现屏幕像素高效操作》这篇文章主要为大家详细介绍了Python如何利用自带模块实现屏幕像素高效操作,文中的示例代码讲解详,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、获取屏幕放缩比例2、获取屏幕指定坐标处像素颜色3、一个简单的使用案例4、总结1、获取屏幕放缩比例from

通过prometheus监控Tomcat运行状态的操作流程

《通过prometheus监控Tomcat运行状态的操作流程》文章介绍了如何安装和配置Tomcat,并使用Prometheus和TomcatExporter来监控Tomcat的运行状态,文章详细讲解了... 目录Tomcat安装配置以及prometheus监控Tomcat一. 安装并配置tomcat1、安装

Python中操作Redis的常用方法小结

《Python中操作Redis的常用方法小结》这篇文章主要为大家详细介绍了Python中操作Redis的常用方法,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解一下... 目录安装Redis开启、关闭Redisredis数据结构redis-cli操作安装redis-py数据库连接和释放增

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允