OpenCV 库来捕获和处理视频输入和相似度测量(73)

 返回:OpenCV系列文章目录(持续更新中......)
上一篇:OpenCV的周期性噪声去除滤波器(70)
下一篇 :使用 OpenCV 创建视频(74)

目标

如今,拥有数字视频录制系统供您使用是很常见的。因此,您最终会遇到不再处理一批图像,而是处理视频流的情况。这些可能有两种类型:实时图像馈送(在网络摄像头的情况下)或预先录制的硬盘驱动器存储文件。幸运的是,OpenCV以相同的方式处理这两者,使用相同的C++类。因此,以下是您将在本教程中学到的内容:

  • 如何打开和阅读视频流
  • 检查图像相似度的两种方法:PSNR 和 SSIM

C++源代码


作为使用 OpenCV 展示这些内容的测试用例,我创建了一个小程序,可以读取两个视频文件并在它们之间执行相似性检查。您可以使用它来检查新的视频压缩算法的工作情况。让我们有一个参考(原始)视频,比如这个小的 Megamind 剪辑和它的压缩版本。您还可以在 OpenCV 源库的文件夹samples/data中找到源代码和这些视频文件。

#include <iostream> // for standard I/O
#include <string> // for strings
#include <iomanip> // for controlling float print precision
#include <sstream> // string to number conversion
 
#include <opencv2/core.hpp> // Basic OpenCV structures (cv::Mat, Scalar)
#include <opencv2/imgproc.hpp> // Gaussian Blur
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp> // OpenCV window I/O
 
using namespace std;
using namespace cv;
 
double getPSNR ( const Mat& I1, const Mat& I2);
Scalar getMSSIM( const Mat& I1, const Mat& I2);
 
static void help()
{
 cout
 << "------------------------------------------------------------------------------" << endl
 << "This program shows how to read a video file with OpenCV. In addition, it "
 << "tests the similarity of two input videos first with PSNR, and for the frames "
 << "below a PSNR trigger value, also with MSSIM." << endl
 << "Usage:" << endl
 << "./video-input-psnr-ssim <referenceVideo> <useCaseTestVideo> <PSNR_Trigger_Value> <Wait_Between_Frames> " << endl
 << "--------------------------------------------------------------------------" << endl
 << endl;
}
 
int main(int argc, char *argv[])
{
 help();
 
 if (argc != 5)
 {
 cout << "Not enough parameters" << endl;
 return -1;
 }
 
 stringstream conv;
 
 const string sourceReference = argv[1], sourceCompareWith = argv[2];
 int psnrTriggerValue, delay;
 conv << argv[3] << endl << argv[4]; // put in the strings
 conv >> psnrTriggerValue >> delay; // take out the numbers
 
 int frameNum = -1; // Frame counter
 
 VideoCapture captRefrnc(sourceReference), captUndTst(sourceCompareWith);
 
 if (!captRefrnc.isOpened())
 {
 cout << "Could not open reference " << sourceReference << endl;
 return -1;
 }
 
 if (!captUndTst.isOpened())
 {
 cout << "Could not open case test " << sourceCompareWith << endl;
 return -1;
 }
 
 Size refS = Size((int) captRefrnc.get(CAP_PROP_FRAME_WIDTH),
 (int) captRefrnc.get(CAP_PROP_FRAME_HEIGHT)),
 uTSi = Size((int) captUndTst.get(CAP_PROP_FRAME_WIDTH),
 (int) captUndTst.get(CAP_PROP_FRAME_HEIGHT));
 
 if (refS != uTSi)
 {
 cout << "Inputs have different size!!! Closing." << endl;
 return -1;
 }
 
 const char* WIN_UT = "Under Test";
 const char* WIN_RF = "Reference";
 
 // Windows
 namedWindow(WIN_RF, WINDOW_AUTOSIZE);
 namedWindow(WIN_UT, WINDOW_AUTOSIZE);
 moveWindow(WIN_RF, 400 , 0); //750, 2 (bernat =0)
 moveWindow(WIN_UT, refS.width, 0); //1500, 2
 
 cout << "Reference frame resolution: Width=" << refS.width << " Height=" << refS.height
 << " of nr#: " << captRefrnc.get(CAP_PROP_FRAME_COUNT) << endl;
 
 cout << "PSNR trigger value " << setiosflags(ios::fixed) << setprecision(3)
 << psnrTriggerValue << endl;
 
 Mat frameReference, frameUnderTest;
 double psnrV;
 Scalar mssimV;
 
 for(;;) //Show the image captured in the window and repeat
 {
 captRefrnc >> frameReference;
 captUndTst >> frameUnderTest;
 
 if (frameReference.empty() || frameUnderTest.empty())
 {
 cout << " < < < Game over! > > > ";
 break;
 }
 
 ++frameNum;
 cout << "Frame: " << frameNum << "# ";
 
 psnrV = getPSNR(frameReference,frameUnderTest);
 cout << setiosflags(ios::fixed) << setprecision(3) << psnrV << "dB";
 
 if (psnrV < psnrTriggerValue && psnrV)
 {
 mssimV = getMSSIM(frameReference, frameUnderTest);
 
 cout << " MSSIM: "
 << " R " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[2] * 100 << "%"
 << " G " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[1] * 100 << "%"
 << " B " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[0] * 100 << "%";
 }
 
 cout << endl;
 
 imshow(WIN_RF, frameReference);
 imshow(WIN_UT, frameUnderTest);
 
 char c = (char)waitKey(delay);
 if (c == 27) break;
 }
 
 return 0;
}
 
// ![get-psnr]
double getPSNR(const Mat& I1, const Mat& I2)
{
 Mat s1;
 absdiff(I1, I2, s1); // |I1 - I2|
 s1.convertTo(s1, CV_32F); // cannot make a square on 8 bits
 s1 = s1.mul(s1); // |I1 - I2|^2
 
 Scalar s = sum(s1); // sum elements per channel
 
 double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels
 
 if( sse <= 1e-10) // for small values return zero
 return 0;
 else
 {
 double mse = sse / (double)(I1.channels() * I1.total());
 double psnr = 10.0 * log10((255 * 255) / mse);
 return psnr;
 }
}
// ![get-psnr]
 
// ![get-mssim]
 
Scalar getMSSIM( const Mat& i1, const Mat& i2)
{
 const double C1 = 6.5025, C2 = 58.5225;
 /***************************** INITS **********************************/
 int d = CV_32F;
 
 Mat I1, I2;
 i1.convertTo(I1, d); // cannot calculate on one byte large values
 i2.convertTo(I2, d);
 
 Mat I2_2 = I2.mul(I2); // I2^2
 Mat I1_2 = I1.mul(I1); // I1^2
 Mat I1_I2 = I1.mul(I2); // I1 * I2
 
 /*************************** END INITS **********************************/
 
 Mat mu1, mu2; // PRELIMINARY COMPUTING
 GaussianBlur(I1, mu1, Size(11, 11), 1.5);
 GaussianBlur(I2, mu2, Size(11, 11), 1.5);
 
 Mat mu1_2 = mu1.mul(mu1);
 Mat mu2_2 = mu2.mul(mu2);
 Mat mu1_mu2 = mu1.mul(mu2);
 
 Mat sigma1_2, sigma2_2, sigma12;
 
 GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
 sigma1_2 -= mu1_2;
 
 GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
 sigma2_2 -= mu2_2;
 
 GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
 sigma12 -= mu1_mu2;
 
 Mat t1, t2, t3;
 
 t1 = 2 * mu1_mu2 + C1;
 t2 = 2 * sigma12 + C2;
 t3 = t1.mul(t2); // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
 
 t1 = mu1_2 + mu2_2 + C1;
 t2 = sigma1_2 + sigma2_2 + C2;
 t1 = t1.mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
 
 Mat ssim_map;
 divide(t3, t1, ssim_map); // ssim_map = t3./t1;
 
 Scalar mssim = mean(ssim_map); // mssim = average of ssim map
 return mssim;
}
// ![get-mssim]

如何读取视频流(在线摄像机或离线文件)?

从本质上讲,视频操作所需的所有功能都集成在 cv::VideoCapture C++ 类中。这本身是建立在 FFmpeg 开源库之上的。这是 OpenCV 的基本依赖项,因此您无需担心这一点。视频由一系列图像组成,我们在文献中将这些图像称为帧。对于视频文件,有一个帧速率指定两帧之间的长度。虽然对于摄像机来说,它们通常每秒可以数字化多少帧是有限制的,但这个属性并不那么重要,因为摄像机在任何时候都能看到世界的当前快照。

您需要执行的第一个任务是将其源代码分配给 cv::VideoCapture 类。您可以通过 cv::VideoCapture::VideoCapture 或其 cv::VideoCapture::open 函数执行此操作。如果此参数为整数,则将类绑定到相机、设备。此处传递的数字是设备的 ID,由操作系统分配。如果您的系统连接了单个摄像头,则其 ID 可能为零,并且从那里开始进一步增加。如果传递给这些参数的参数是字符串,它将引用视频文件,并且字符串指向文件的位置和名称。例如,对于上面的源代码,有效的命令行是:

video/Megamind.avi video/Megamind_bug.avi 35 10

我们进行相似性检查。这需要参考和测试用例视频文件。前两个参数指的是这一点。这里我们使用一个相对地址。这意味着应用程序将查看其当前工作目录并打开视频文件夹,并尝试在其中查找Megamind.aviMegamind_bug.avi

const string sourceReference = argv[1],sourceCompareWith = argv[2];
 
VideoCapture captRefrnc(sourceReference);
// or
VideoCapture captUndTst;
captUndTst.open(sourceCompareWith);

若要检查类与视频源的绑定是否成功,请使用 cv::VideoCapture::isOpened 函数:

if ( !captRefrnc.isOpened())
 {
 cout << "Could not open reference " << sourceReference << endl;
 return -1;
 }

调用对象析构函数时,自动关闭视频。但是,如果要在此之前关闭它,则需要调用其 cv::VideoCapture::release 函数。视频的帧只是简单的图像。因此,我们只需要从 cv::VideoCapture 对象中提取它们并将它们放入 Mat 对象中。视频流是连续的。您可以通过 cv::VideoCapture::read 或重载的 >> 运算符一个接一个地获取帧:

Mat frameReference, frameUnderTest;
captRefrnc >> frameReference;
captUndTst.read(frameUnderTest);

如果无法获取任何帧(导致视频流关闭或视频文件末尾),则上面的读取操作将空 Mat 对象。如果出现以下情况,我们可以通过一个简单的方法来检查这一点:

if( frameReference.empty() || frameUnderTest.empty())
{
 // exit the program
}

读取方法由帧抓取和应用的解码组成。可以使用 cv::VideoCapture::grab 和 cv::VideoCapture::retrieve 函数显式调用这两个函数。

除了帧的内容之外,视频还附加了许多信息。这些通常是数字,但在某些情况下,它可能是短字符序列(4 个字节或更少)。因此,为了获取这些信息,有一个名为 cv::VideoCapture::get 的通用函数返回包含这些属性的双精度值。使用按位运算对有效值仅为整数的双精度类型和转换中的字符进行解码。它的单个参数是查询属性的 ID。例如,这里我们得到参考和测试用例视频文件中的帧大小;加上参照内部的帧数。

Size refS = Size((int) captRefrnc.get(CAP_PROP_FRAME_WIDTH),
 (int) captRefrnc.get(CAP_PROP_FRAME_HEIGHT)),
 
cout << "Reference frame resolution: Width=" << refS.width << " Height=" << refS.height
 << " of nr#: " << captRefrnc.get(CAP_PROP_FRAME_COUNT) << endl;

在处理视频时,您可能经常希望自己控制这些值。为此,有一个 cv::VideoCapture::set 函数。它的第一个参数仍然是要更改的属性的名称,第二个参数是包含要设置的值的 double 类型。如果成功,它将返回 true,否则返回 false。很好的例子是在视频文件中寻找给定的时间或帧:

captRefrnc.set(CAP_PROP_POS_MSEC, 1.2); // go to the 1.2 second in the video
captRefrnc.set(CAP_PROP_POS_FRAMES, 10); // go to the 10th frame of the video
// now a read operation would read the frame at the set position

对于可以读取和更改的属性,请查看 cv::VideoCapture::get 和 cv::VideoCapture::set 函数的文档。

图像相似度 - PSNR 和 SSIM

我们想检查我们的视频转换操作有多难以察觉,因此我们需要一个系统来逐帧检查相似性或差异性。最常用的算法是PSNR(又名峰值信噪比)。最简单的定义是从均方误差开始的。假设有两个图像:I1 和 I2;具有二维大小的 i 和 j,由 c 个通道组成。

然后 PSNR 表示为:

这里的 (MAX_I)是像素的最大有效值。对于简单的单字节图像,每个通道每像素,这是 255。当两个图像相同时,MSE 将给出零,从而导致 PSNR 公式中的除以零运算无效。在这种情况下,PSNR 是未定义的,因此我们需要单独处理这种情况。之所以过渡到对数刻度,是因为像素值具有非常宽的动态范围。所有这些都转换为 OpenCV,函数如下所示:

double getPSNR(const Mat& I1, const Mat& I2)
{
 Mat s1;
 absdiff(I1, I2, s1); // |I1 - I2|
 s1.convertTo(s1, CV_32F); // cannot make a square on 8 bits
 s1 = s1.mul(s1); // |I1 - I2|^2
 
 Scalar s = sum(s1); // sum elements per channel
 
 double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels
 
 if( sse <= 1e-10) // for small values return zero
 return 0;
 else
 {
 double mse = sse / (double)(I1.channels() * I1.total());
 double psnr = 10.0 * log10((255 * 255) / mse);
 return psnr;
 }
}

通常,视频压缩的结果值介于 30 到 50 之间,越高越好。如果图像明显不同,您将得到更低的图像,例如 15 左右。这种相似性检查计算起来既简单又快捷,但在实践中,它可能与人眼感知有些不一致。结构相似性算法旨在纠正这一点。

描述这些方法远远超出了本教程的目的。为此,我邀请您阅读介绍它的文章。尽管如此,您可以通过查看下面的 OpenCV 实现来获得它的良好图像。

注意

SSIM在以下文章中进行了更深入的描述:“Z. Wang, A. C. Bovik, H. R. Sheikh and E. P. Simoncelli,“图像质量评估:从错误可见性到结构相似性”,IEEE Transactions on Image Processing,第 13 卷,第 4 期,第 600-612 页,2004 年 4 月。

Scalar getMSSIM( const Mat& i1, const Mat& i2)
{
 const double C1 = 6.5025, C2 = 58.5225;
 /***************************** INITS **********************************/
 int d = CV_32F;
 
 Mat I1, I2;
 i1.convertTo(I1, d); // cannot calculate on one byte large values
 i2.convertTo(I2, d);
 
 Mat I2_2 = I2.mul(I2); // I2^2
 Mat I1_2 = I1.mul(I1); // I1^2
 Mat I1_I2 = I1.mul(I2); // I1 * I2
 
 /*************************** END INITS **********************************/
 
 Mat mu1, mu2; // PRELIMINARY COMPUTING
 GaussianBlur(I1, mu1, Size(11, 11), 1.5);
 GaussianBlur(I2, mu2, Size(11, 11), 1.5);
 
 Mat mu1_2 = mu1.mul(mu1);
 Mat mu2_2 = mu2.mul(mu2);
 Mat mu1_mu2 = mu1.mul(mu2);
 
 Mat sigma1_2, sigma2_2, sigma12;
 
 GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
 sigma1_2 -= mu1_2;
 
 GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
 sigma2_2 -= mu2_2;
 
 GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
 sigma12 -= mu1_mu2;
 
 Mat t1, t2, t3;
 
 t1 = 2 * mu1_mu2 + C1;
 t2 = 2 * sigma12 + C2;
 t3 = t1.mul(t2); // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
 
 t1 = mu1_2 + mu2_2 + C1;
 t2 = sigma1_2 + sigma2_2 + C2;
 t1 = t1.mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
 
 Mat ssim_map;
 divide(t3, t1, ssim_map); // ssim_map = t3./t1;
 
 Scalar mssim = mean(ssim_map); // mssim = average of ssim map
 return mssim;
}

这将返回图像每个通道的相似性索引。此值介于 0 和 1 之间,其中 1 对应于完全拟合。不幸的是,许多高斯模糊的成本非常高,因此虽然 PSNR 可以在实时环境中工作(每秒 24 帧),但这比实现类似的性能结果要多得多。

因此,本教程开头提供的源代码将对每个帧执行 PSNR 测量,并且仅对 PSNR 低于输入值的帧执行 SSIM。出于可视化目的,我们在 OpenCV 窗口中显示两个图像,并将 PSNR 和 MSSIM 值打印到控制台。期待看到类似的东西:

您可以在 YouTube 上观察此操作时实例。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

连锁收银系统总仓到门店库存调拨操作教程

1、进入系统后台&#xff0c;系统后台登录网址&#xff1a; 2、点击商品>门店调拨 3、选择调出仓库和调入门店 4、可选择添加商品逐个进行调拨&#xff0c;也可以批量导入需要调拨的商品 然后点击确定。 5、新增调拨后&#xff0c;系统会显示“待出库”状态 6、仓库已经准备…

Python 中使用私有成员的子类化

1、问题背景 Python 语言中&#xff0c;变量名与访问器同名是一个非常好的特性&#xff1a; self.__value 1def value():return self.__value但是&#xff0c;当我们想要子类化一个类&#xff0c;并访问其私有成员时&#xff0c;却没有一种简单的方法。通常&#xff0c;我们…

高速、简单、安全的以太彩光,锐捷网络发布极简以太全光 3.X 方案

从 2021 年 3 月正式推出到现在&#xff0c;锐捷网络极简以太全光方案已经走进第四个年头。IT 仍在不断向前发展&#xff0c;数字化进程深入&#xff0c;数字化业务增多&#xff0c;更广泛的终端设备接入企业级园区网络&#xff0c;对园区网络提出了更高的要求&#xff0c;例如…

Flutter开发Dart中的队列(Queue)

文章目录 Dart中的队列&#xff08;Queue&#xff09;基本操作示例队列的类型队列的应用总结 Dart中的队列&#xff08;Queue&#xff09; 队列是一种抽象的数据结构&#xff0c;遵循“先进先出”&#xff08;FIFO&#xff09;的原则。这意味着最早添加的元素将首先被移除。队…

PS路径文字怎么变换的?

如果网友们没有用过钢笔工具&#xff0c;画好后的样子是什么&#xff0c;建议你看看这个方法&#xff01; 建立的路径之后&#xff0c;在编辑菜单栏里单击。 选择变换路径&#xff0c;可以改变路径文字的方向&#xff0c;点击垂直翻转即可完成方向的改变&#xff01;

vue3+vite+axios+ElementPlus+ElLoading简易封装

1.安装按需加载element-plus需要的依赖包 pnpm install element-pluspnpm install axios# 按需自动导入 pnpm install -D unplugin-vue-components unplugin-auto-import# 自动导入element-plus样式 pnpm install -D vite-plugin-style-import2.修改jsconfig.json {"com…

【iOS】方法交换(Method Swizzling)

文章目录 前言一、原理与注意用法注意要点Method Swizzing涉及的相关API 二、应用场景与实践1.统计VC加载次数并打印2.防止UI控件短时间多次激活事件3.防崩溃处理&#xff1a;数组越界问题4.防KVO崩溃 总结 前言 上文讲到了iOS的消息发送机制&#xff0c;在消息机制中我们了解…

【革命启示录】Spring框架:Java开发的“核聚变”能量源!

Hello&#xff0c;我是阿佑&#xff0c;今天给大家整的活是 《Java开发的“核聚变”能量源》 文章目录 Spring框架原理详解一、引言简介目的特点例子 二、背景介绍问题解决方案例子 三、核心概念3.1 控制反转&#xff08;Inversion of Control, IoC&#xff09;定义实现例子与代…

04-28 周日 FastAPI Post请求同时传递文件和普通参数

04-28 周日 FastAPI Post请求同时传递文件和普通参数 时间版本修改人描述04-28 周日V0.1宋全恒新建文档2024年5月6日14:20:05V1.0宋全恒完成文档的传递 简介 由于在重构FastBuild的时候&#xff0c;为了支持TLS是否启用&#xff0c;在接口中需要同时传递文件参数和其他参数&am…

应急响应靶机训练-近源渗透OS-1

前言 应急响应靶机训练&#xff0c;为保证每位安服仔都有上手的机会&#xff0c;不做理论学家&#xff0c;增加动手经验&#xff0c;可前来挑战应急响应靶机-近源渗透OS1,此系列后期会长期更新&#xff0c;关注本公众号&#xff0c;被动学习。 挑战内容 前景需要&#xff1a;…

Spring Gateway的核心功能:路由、过滤、限流一网打尽

Spring Gateway的简介 在微服务架构的世界里&#xff0c;如同繁星点点的服务需要一个指挥家&#xff0c;将它们有序地组织起来&#xff0c;让它们能够和谐地协同工作。这个指挥家&#xff0c;就是Spring Gateway。它是一个基于Spring Framework 5、Project Reactor和Spring Bo…

Java多线程:常见的线程的创建方法及Thread类详解

目录 一.并发编程相关概念 线程与进程 多线程 Java中线程的状态 二.线程的创建方法 方法一&#xff1a;继承Thread类 方法二&#xff1a;实现Runnable接口 其他方法 三.Thread类详解 Thread常见构造方法 Thread常见属性 Thread常见方法 start() 与 run() sleep(…

使用代理IP时,如何预防未知的风险?

在使用代理IP时&#xff0c;预防未知的风险是至关重要的。代理IP虽然提供了诸多便利&#xff0c;如匿名浏览、访问控制和内容过滤等&#xff0c;但如果不加以妥善管理和使用&#xff0c;可能会面临数据泄露、隐私暴露、恶意活动关联等风险。以下是一些建议&#xff0c;以帮助您…

Java中的maven的安装和配置

maven的作用 依赖管理 方便快捷的管理项目依赖的资源&#xff0c;避免版本冲突问题 统一项目管理 提供标准&#xff0c;统一的项目结构 项目构建 标准跨平台&#xff08;Linux、windows、MacOS&#xff09;的自动化项目构建方式 maven的安装和配置 在maven官网下载maven Ma…

如何用Kimi,5秒1步生成流程图

引言 在当前快节奏的工作环境中&#xff0c;拥有快速、专业且高效的工具不可或缺。 Kimi不仅能在5秒内生成专业的流程图&#xff08;kimi&#xff09;&#xff0c;还允许实时编辑和预览&#xff0c;大幅简化了传统流程图的制作过程。 这种迅速的生成能力和高度的可定制性使得…

如何使用低代码快速创建一个复杂交叉报表?

前言 在当今数字化时代&#xff0c;数据是企业决策和发展的重要支柱。为了更好地理解和利用数据&#xff0c;生成清晰、全面的报表至关重要。而复杂交叉报表作为一种高级数据分析工具&#xff0c;能够帮助企业深入挖掘数据背后的价值&#xff0c;提供全面的数据概览和分析结果…

数据分析——业务指标量化

业务指标量化 前言一、统计指标二、统计指标特点完整的统计指标统计指标的理解和使用方法 三、统计指标类型总量指标时期指标时点指标总量指标的作用 相对指标计划完成相对数指标结构相对数指标比例相对数指标比较相对数指标动态相对数指标 平均指标 四、数量指标和质量指标五、…

Java设计模式 _结构型模式_代理模式(静态,动态)

一、基础概念 1、代理模式 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式。它允许我们通过添加一个代理对象来控制对另一个对象的访问&#xff0c;从而实现一些额外的功能&#xff0c;如访问控制、日志记录、性能监控等。代理模式主要分为静态代理和动态…

Versatile Diffusion—— 融合文本和图像的扩散模型

介绍 Diffusion模型在各种生成任务中取得了显著的进展&#xff0c;成为了一个重要的里程碑。特别是像DALLE 2、Imagen和Stable Diffusion&#xff08;SD&#xff09;这样的模型&#xff0c;不仅在学术界引起了广泛关注&#xff0c;也在工业界产生了深远影响。尽管这些模型在特…

10.Java对象内置结构

文章目录 Java对象内置结构1.Java对象的三个部分1.1.对象头1.2.对象体1.3.对齐字节 2.对象结构中核心字段的作用2.1.MarkWord(标记字)2.2.Class Pointer(类对象指针)2.3.Array Length(数组长度)2.4.对象体2.5.对齐字节 3.Mark Word的结构信息3.1.不同锁状态下的Mark Word字段结…