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

news2024/11/15 13:41:01

 返回: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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1545512.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

简易电路设计,PW1605芯片实现24V/30V/48V限流过压保护功能

一般描述 PW1605 是一款电流限制开关&#xff0c;具有可编程输入过压保护和输出电压箝位功能。集成保护 N 沟道 FET 具有极低的 RDS&#xff08;ON&#xff09; 功能&#xff0c;PW1605有助于降低正常工作期间的功率损耗。可编程软启动时间控制启动期间输出电压的压摆率。独立的…

本周四Techtalk技术交流社区邀请吕海波老师为大家带来精彩技术分享

欢迎您关注我的公众号【尚雷的驿站】 **************************************************************************** 公众号&#xff1a;尚雷的驿站 CSDN &#xff1a;https://blog.csdn.net/shlei5580 墨天轮&#xff1a;https://www.modb.pro/u/2436 PGFans&#xff1a;ht…

Docker - 哲学 默认网络和 自定义网络 与 linux 网络类型 和 overlay2

默认网络&#xff1a;不指定 --nerwork 不指定 网络 run 一个容器时&#xff0c;会直接使用默认的网络桥接器 &#xff08;docker0&#xff09; 自定义网络&#xff1a;指定 --nerwork 让这两台容器互相通信 的前提 - 共享同一个网络 关于 ip addr 显示 ens160 储存驱动 ov…

智慧公厕,运用大数据提升公共厕所管理水平

在现代社会&#xff0c;科技的发展给我们带来了诸多便利&#xff0c;而智慧公厕就是其中之一。智慧公厕运用数据和技术&#xff0c;提升公共厕所的管理水平&#xff0c;为社会生活服务。本文将以智慧公厕源头实力厂家广州中期科技有限公司&#xff0c;遍布全国的众多标杆性案例…

macOS Sonoma 14.4.1 (23E224) 正式版发布,ISO、IPSW、PKG 下载

macOS Sonoma 14.4.1 (23E224) 正式版发布&#xff0c;ISO、IPSW、PKG 下载 2024 年 3 月 26 日凌晨&#xff0c;macOS Sonoma 14.4.1 更新修复了一个可能导致连接到外部显示器的 USB 集线器无法被识别的问题。它还解决了可能导致 Java 应用程序意外退出的问题&#xff0c;并修…

【电力监控保护】AM5SE-IS防孤岛保护装置/35kV、10kV、380V分布式光伏并网供电/什么是孤岛效应/孤岛效应的危害

什么是孤岛效应&#xff01;&#xff01;&#xff01; 安科瑞薛瑶瑶18701709087 在电力系统中&#xff0c;孤岛效应指的是当电网突然断电时&#xff0c;并网光伏发电系统仍然保持对电网中部分线路的供电状态。这种情况下&#xff0c;这些线路与其他电网断开&#xff0c;形成了…

设置远程访问 jupyter Notebook Lab

安装Anaconda / Miniconda 进入conda环境&#xff0c;安装jupyter https://jupyter.org/install 生成notebook config C:\Users\***>jupyter notebook --generate-config Writing default config to: C:\Users\***\.jupyter\jupyter_notebook_config.py创建密码 jupyter…

git cherry pick merge部分提交

cherry pick merge 指定某次提交 1. git history 选择要从哪个分支merge 2. 找到提交记录,选择cherry pick 3.这个时候就可以直接push了

【Leetcode每日一题】 动态规划 - 解码方法(难度⭐)(43)

1. 题目解析 题目链接&#xff1a;91. 解码方法 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 这是一道类似斐波那契数列的题目~ 当我们遇到一个类似斐波那契数列的问题时&#xff0c;我们通常会想到使用动态规划&…

计算机网络(一)体系结构

计算机网络体系结构 1.计算机网络概述1.1 概念1.1.1 计算机网络、互连网、互联网 1.2 组成1.3 功能1.4 分类1.5 性能指标 2.体系结构和参考模型2.1 分层结构&#xff0c;协议、接口、服务2.2 ISO/OSI参考模型和TCP/IP参考模型 1.计算机网络概述 1.1 概念 计算机网络是一个将众…

JavaSE—IO流之字符流与字节流

前言&#xff1a;按照数据的传输方向分为输入流和输出流&#xff1b;流也可以从数据编码格式上划分为字节流和字符流&#xff1b;根据封装类型不同流又分为节点流和处理流。 &#x1f4cc; 字节流 &#x1f4cc; 字节流中常用类及基本方法&#xff1a; 字节输入流 …

水泊梁山108小酒坛之小李广花荣

小李广花荣&#xff0c;是中国著名小说《水浒传》中的108将之一&#xff0c;有“百步穿杨”的功夫。在梁山泊英雄中排行第九&#xff0c;为马军八虎骑兼先锋使之首。原是清风寨副知寨&#xff0c;使一杆银枪&#xff0c;一张弓射遍天下无敌手&#xff0c;生得一双俊目&#xff…

如何使用生成式人工智能进行科学文献检索?

在学术写作过程中&#xff0c;查找文献是一个至关重要的步骤。文献检索不仅可以帮助我们了解研究领域的现状和发展&#xff0c;还可以为我们的论文提供理论支持和数据来源。然而&#xff0c;许多学者在查找文献时往往感到困惑和无所适从。本文将详细解释如何查找文献&#xff0…

java解决跨域问题

浏览器js在访问服务器中的资源时&#xff0c;会出现同一页面或者不同域名(协议&#xff0c;IP&#xff0c;端口)不可访问 例如:file://d://test.html页面(file协议)中通过ajax访问服务器api.test.com的接口(http协议)&#xff0c;由于协议不同&#xff0c;此时会出现浏览器访问…

基于数据沙箱与LLM用例自愈的UI自动化测试平台

本期作者 项目参与人员&#xff1a; 顾伊凡、陈钰广、张又中、杨雨浩、樊执政、熊梦园、何璇、谭楠 UI自动化测试能够在一定程度上确保产品质量&#xff0c;尤其在降本提效的大背景下&#xff0c;其重要性愈发凸显。理想情况下&#xff0c;UI自动化测试不仅能够能帮我们规避不少…

低功率接地故障断路器(GFI)控制芯片D4147描述

D4147主要用于三线制GFCI输出接口、GFCI芯片断路器、便携式GFCI线路等领域的产品&#xff0c;侦测并防护火线对地故障和零线对负载短路故障。 功能介绍 D4147 为低功率接地故障断路器&#xff08;GFI&#xff09;控制器芯片&#xff0c;用于检测危险的接地故障电流路径以及接地…

基于nodejs+vue文学创作的社交论坛python-flask-django-php

课题主要采用nodejs技术和MySQL数据库技术以及express框架进行开发。系统主要包括个人中心、用户管理、文章类型管理、文章信息管理、文章举报管理、警告信息管理、系统管理等功能&#xff0c;从而实现智能化的社交论坛管理方式&#xff0c;提高社交论坛管理的效率。 前端技术&…

利用Python和IP技术实现智能旅游情报系统

文章目录 引言一、系统架构设计1. 数据采集模块2. 数据处理模块3. 用户界面模块 二、数据获取技术应用三、系统功能展示四、亮数据采集工具介绍五、总结六、号外 引言 随着旅游行业的不断发展&#xff0c;人们对旅游信息的需求也越来越大。为了帮助旅行者更好地规划行程&#…

Springboot:Actuator监控

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 一、Actuator介绍 二、集成步骤 三、重要端点介绍 1、/actuator 2、/actuator/env 3、/actuator/heapdump 4、/actuator/metrics 5、/actuator/shutdown 6、/l…

通过MobaXterm工具可视化服务器桌面

一、MobaXterm工具 MobaXterm是一款功能强大的远程连接工具&#xff0c;可以连接到各种类型的服务器&#xff0c;包括Linux、Windows和MacOS。支持多种协议&#xff0c;包括SSH、RDP、VNC和Telnet MobaXterm可以通过X11转发功能可视化服务器桌面。 二、MobaXterm工具可视化服务…