0
点赞
收藏
分享

微信扫一扫

LeetCode 面试经典150题 242.有效的字母异位词

天蓝Sea 03-29 19:30 阅读 2

 返回: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 = 255
 if sum_value < 0:
 sum_value = 0
 return sum_value
def sharpen(my_image):
 if is_grayscale(my_image):
 height, width = my_image.shape
 else:
 my_image = cv.cvtColor(my_image, cv.CV_8U)
 height, width, n_channels = my_image.shape
 result = 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_COLOR
 if argv:
 filename = sys.argv[1]
 if len(argv) >= 2 and sys.argv[2] == "G":
 img_codec = cv.IMREAD_GRAYSCALE
 src = 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 -1
 cv.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 = 255
 if sum_value < 0:
 sum_value = 0
 return sum_value
def sharpen(my_image):
 if is_grayscale(my_image):
 height, width = my_image.shape
 else:
 my_image = cv.cvtColor(my_image, cv.CV_8U)
 height, width, n_channels = my_image.shape
 result = 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 = 255
 if sum_value < 0:
 sum_value = 0
 return sum_value
def sharpen(my_image):
 if is_grayscale(my_image):
 height, width = my_image.shape
 else:
 my_image = cv.cvtColor(my_image, cv.CV_8U)
 height, width, n_channels = my_image.shape
 result = 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

举报

相关推荐

0 条评论