【ZYNQ入门】第十篇、基于FPGA的图像白平衡算法实现

news2024/11/24 15:03:58

目录

第一部分、关于白平衡的知识    

1、MATLAB 自动白平衡算法的实现

1.1、matlab代码

1.2、测试效果

1.3 测试源图

2、为什么摄像头采集的图像要做白平衡

3、自动白平衡算法总结

4、FPGA设计思路

4.1、实时白平衡的实现

4.2、计算流程优化思路  

第二部分、硬件实现

1、除法IP核的调用方法

2、乘法IP核的调用方法

3、verilog代码

第三部分、实现结果

1、白平衡前后对比

2、总结


第一部分、关于白平衡的知识    

1、MATLAB 自动白平衡算法的实现

        大家先测试下面这段自动白平衡MATLAB代码,代码来源于以下这篇博客我只不过加上了注释,更多细节请大家参考这篇博客:图像白平衡原理及实现-CSDN博客    

1.1、matlab代码

%%白平衡与色温紧密相关,不同色温光源下图像会呈现不同程度的偏色
%%由于人眼独特的适应性,在不同光照条件下观看物体时不会出现偏色,而就没这么先进了
%%蓝色光色温高,红色光色温低
%CSDN:https://blog.csdn.net/helimin12345/article/details/78255669
 
clc;
clear all;
close all;

tic;%用来记录程序的使用时间,tic 程序 toc

% imgSrc = imread('green1.jpg');
imgSrc = imread('test2.png');
%定义一个与图像大小一样的三位变量
imgDst = imgSrc;
%将RGB三个通道的数据进行分离
imgR = imgSrc(:,:,1);%单个通道的数据默认 uint8(0~255)
imgG = imgSrc(:,:,2);
imgB = imgSrc(:,:,3);
%对RGB三个通道的像素求均值,相当于mean( mean( A ) ) 即对整一个矩阵求像素平均值
RAve = mean2(imgR);
GAve = mean2(imgG);
BAve = mean2(imgB);
aveGray = (RAve + GAve + BAve) / 3;%比较系数
%计算三个通道的增益系数
RCoef = aveGray / RAve;
GCoef = aveGray / GAve;
BCoef = aveGray / BAve;
%使用增益系数来调整原始图
%注意:其实这里应该是存在一个比较的过程(小于255,那就是当前值;大于255,那就等于255),只不过uint8超出了255自动溢出了
%巧妙的运用溢出,会减少很多计算量
RCorrection = RCoef * imgR;
GCorrection = GCoef * imgG;
BCorrection = BCoef * imgB;
%白平衡后的图像
imgDst(:,:,1) = RCorrection;
imgDst(:,:,2) = GCorrection;
imgDst(:,:,3) = BCorrection;
%显示两张图片
figure(1),imshow(imgSrc),title('original image');
figure(2),imshow(imgDst),title('white balanced image');

1.2、测试效果

        关于这两张测试照片,我也会放在文末。先看效果

1.3 测试源图

        我也是网上从别人博客保存的,找不到原博客了😂

2、为什么摄像头采集的图像要做白平衡

        人眼对白色很敏感, 在不同色温下,都能准确判断出白色。例如下面那张图,色温比较高的时候会偏蓝,色温比较低的的时候会偏红

        当摄像头在不同色温的光源下,采集的图像会出现不同程度的偏色,与人眼看到的颜色不一致,因此需要进行白平衡处理。(还有一点,就是我感觉CMOS摄像头就算在正常光源下,采集的图像也是偏绿的,不知道是不是因为选用的Bayer转RGB的算法太垃圾了,还是什么别的原因。关于Bayer转RGB的算法大家可以看这篇:基于FPGA的Bayer转RGB算法实现)

        但是目前稍微好一点的摄像头模组,都包含了自动白平衡算法,都不需要做处理。

3、自动白平衡算法总结

        把上面的matlab代码仔细读几遍,读明白之后,总结出白平衡算法的步骤:

step1、分别对图像的R、G、B三通道的数据进行求和得到Rsum、Gsum、Bsum;

step2、获取图像的R、G、B三通道的平均值Rv,Gv,Bv;imag_width:当前图像的宽,

imag_high:当前图像的高度。

                        Rv  = Rsum /(imag_width*imag_high)

                        Gv  = Gsum /(imag_width*imag_high)

                        Bv  =  Bsum /(imag_width*imag_high)

step3、将求得的Rv、Gv、Bv 进行加和取平均值,得到Kv = (Rv + Gv + Bv) / 3;

step4、分别将R、G、B三通道的数据带入公式进行计算,得到新的值

                        R通道: Rnew = R * Kv / Rv; if(Rnew >255) Rnew = 255;

                        G通道:Gnew  = G * Kv / Gv;if(Gnew >255) Gnew = 255;

                        B通道: Bnew  = B * Kv / Bv; if(Bnew >255) Bnew  = 255;

step5、最后将计算后的图片显示出来,便是白平衡后的图像。

4、FPGA设计思路

4.1、实时白平衡的实现

        FPGA的摄像头采集的是实时的图像数据,每秒采集30帧,那么如何将FPGA获取的图像进行白平衡处理呢?

        解决办法:就是计算当前帧图像的Rv,Gv,Bv,Kv将计算的结果留给下一帧图像使用,下一帧的Rv,Gv,Bv,Kv计算结果又给下下一帧使用...一直循环,就实现了实时的图像白平衡处理

4.2、计算流程优化思路  

        正常情况下,进行除法可以调用除法IP核来解决。但是当除数很大且接近于2^N时,这时就可以用截位的方式来代替除法核。

        例如对于分辨率为1920*1080图片,Rv  = Rsum /(1920*1080),而1920*1080 = 2,073,600非常接近于2^21 = 2,097,152。

        如果我用2^21次幂来代替1920*1080,那么只需要去除Rsum的低21位即可,直接截位,都不需要移位。这这,太牛逼了!!!

计算误差方法:由于误差很小,而且对于图像处理也不需要那么高的精度,所以该方法可行。

(2^21 - (1920*1080)) / (1920*1080)

= (2,097,153 - 2,073,600) / 2,073,600

≈ 0.0113585 ≈ 1.14%

第二部分、硬件实现

1、除法IP核的调用方法

 第一步、搜索,输入divider generator

 第二步、配置第二个界面

 第三步、配置第三个界面

注意:这里的Latency配置为自动模式

第四步、整数有效位宽和余数有效位宽

注意:这里的位宽需要记住,方便后面截位数据

2、乘法IP核的调用方法

第一步、搜索multiplier

第二步、配置第二个界面

第三步、配置Output and control界面

注意:这里是几级流水,那么输出就有几个时钟周期的latency。这里选用系统推荐的流水级数,当然也可以自定手动修改流水级数,级数越多时序越好,延迟越多,因此实际开发要是情况而定。

3、verilog代码

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : BigFartPeach
// CSDN   : 大屁桃 
// E-mail : 2624507313@qq.com
// File   : white_balance_my.v
// Create : 2024-01-06 13:58:39
// -----------------------------------------------------------------------------  
module white_balance_my(
	input wire		clk,
	input wire		rst,
	input wire 		vsync,
	input wire 		hsync,
	input wire [7:0]red,
	input wire [7:0]green,
	input wire [7:0]blue,
	
	output wire		fra_vsy,
	output reg		fra_hsy,

	output wire[23:0]rgb_new
	);

//变量定义
/*累计这一帧,并计算上一帧的累加结果*/
reg [1:0]vsync_dly;//对vsync延迟两拍
reg 	 vsync_fall;//检测vsync下降沿

reg [28:0]Rsum,Gsum,Bsum;//求和
reg	[7:0]Ravg,Gavg,Bavg;//求均值,直接舍去低21位

wire[30:0]Ksum;//Rsum+Gsum+Bsum的和

wire[55:0]Temp_Kavg;//临时用来存储Kavg除法IP输出的结果
wire 	  Kavg_valid;//除法IP核输出的结果有效标志
reg [30:0]Kavg;//除法IP核输出的结果
///
/*计算当前帧*/
wire [18:0]temp_red,temp_green,temp_blue;//用来存储red*Kavg、green*Kavg、blue*Kavg的值
wire [31:0]temp_red_new,temp_green_new,temp_blue_new;//用来存储除法IP输出的值,temp_red/Ravg、temp_green/Gavg、temp_blue/Bavg
wire 	  temp_fra_hsy;//用来存储输出的red_new的valid,用作输出的fra_hsy

//凑成24bit输出
reg [7:0]red_new;
reg [7:0]green_new;
reg [7:0]blue_new;

assign rgb_new = {red_new,green_new,blue_new};//凑成24bit输出

//vsync_dly打拍
always @(posedge clk) begin
	vsync_dly <= {vsync_dly[0],vsync};
end
//检测vsync下降沿
always @(posedge clk or posedge rst) begin
	if (rst == 1'b1) begin
		vsync_fall <= 1'b0;
	end
	else if (vsync_dly == 2'b10) begin
		vsync_fall <= 1'b1;
	end
	else begin
		vsync_fall <= 1'b0;//just one cycle clock
	end
end

//Rsum,Gsum,Bsum求和 以及 清零
always @(posedge clk or posedge rst) begin
	if (rst == 1'b1) begin
		Rsum <= 'd0;
		Gsum <= 'd0;
		Bsum <= 'd0;
	end
	else if (vsync_fall == 1'b1) begin
		Rsum <= 'd0;
		Gsum <= 'd0;
		Bsum <= 'd0;		
	end
	else if(hsync == 1'b1)begin
		Rsum <= Rsum + red;
		Gsum <= Gsum + green;
		Bsum <= Bsum + blue;
	end
end
//Ksum求和
assign Ksum = Rsum + Gsum + Bsum;//如果用alway,相较于Rsum,Gsum,Bsum会晚一个时钟周期

//Ravg,Gavg,Bavg求均值,直接舍去Rsum,Gsum,Bsum的低21位
always @(posedge clk or posedge rst) begin
	if (rst == 1'b1) begin
		Ravg <= 'd0;
		Gavg <= 'd0;
		Bavg <= 'd0;
	end
	else if (vsync_fall == 1'b1) begin
		Ravg <= Rsum[28:21];
		Gavg <= Gsum[28:21];
		Bavg <= Bsum[28:21];
	end
end

//Kavg的除法IP,33个latency
div_gen_Ksum_div_1920x1080x3 div_gen_Kavg (
  .aclk(clk),                                      // input wire aclk
  .s_axis_divisor_tvalid(vsync_fall),    // input wire s_axis_divisor_tvalid
  .s_axis_divisor_tdata(23'd6220800),      // input wire [23 : 0] s_axis_divisor_tdata
  .s_axis_dividend_tvalid(vsync_fall),  // input wire s_axis_dividend_tvalid
  .s_axis_dividend_tdata(Ksum),    // input wire [31 : 0] s_axis_dividend_tdata
  .m_axis_dout_tvalid(Kavg_valid),          // output wire m_axis_dout_tvalid
  .m_axis_dout_tdata(Temp_Kavg)            // output wire [55 : 0] m_axis_dout_tdata
);

//锁存Kavg
always @(posedge clk or posedge rst) begin
	if (rst == 1'b1) begin
		Kavg <= 'd0;
	end
	else if (Kavg_valid == 1'b1) begin//可以用这个信号来锁存Kavg
		Kavg <= Temp_Kavg[54:24];//截取输出的整数位
	end
end



///
//进行结果计算
//red_new的计算,先算乘法,再算除法 red_new = (red*Kavg)/Ravg
//3级流水线,3个latency!!!
mult_gen_8xKavg redxKavg (
  .CLK(clk),  // input wire CLK
  .A(red),      // input wire [7 : 0] A
  .B(Kavg[10:0]),      // input wire [10 : 0] B(这里取11位,主要是根据老师推荐的,按道理是要去大一点,但是大多少位并没有限制)
  .P(temp_red)      // output wire [18 : 0] P
);

/*************************************************/
/******************解决白线问题的方法**************/
/*************************************************/
//给hsync进行打拍操作。delay 3个 latency,和上面乘法核的输出对齐
reg [2:0]hsync_dly;
always @(posedge clk) begin
	hsync_dly <= {hsync_dly[1:0],hsync};
end
/*************************************************/
/*************************************************/

div_gen_0 temp_red_div_Ravg (
  .aclk(clk),                                      // input wire aclk
  .s_axis_divisor_tvalid(hsync_dly[2]),    // input wire s_axis_divisor_tvalid
  .s_axis_divisor_tdata(Ravg),      // input wire [7 : 0] s_axis_divisor_tdata
  .s_axis_dividend_tvalid(hsync_dly[2]),  // input wire s_axis_dividend_tvalid
  .s_axis_dividend_tdata(temp_red),    // input wire [23 : 0] s_axis_dividend_tdata
  .m_axis_dout_tvalid(temp_fra_hsy),          // output wire m_axis_dout_tvalid
  .m_axis_dout_tdata(temp_red_new)            // output wire [31 : 0] m_axis_dout_tdata   [26:8]
);
//red进行判断
always @(posedge clk or posedge rst) begin
	if (rst == 1'b1) begin
		red_new <= 'd0;
	end
	else if (temp_red_new[26:8] > 'd255) begin
		red_new <= 'd255;
	end
	else begin
		red_new <= temp_red_new[15:8];//有效值肯定在这个八位区间
	end
end

//green_new
mult_gen_8xKavg greenxKavg (
  .CLK(clk),  // input wire CLK
  .A(green),      // input wire [7 : 0] A
  .B(Kavg[10:0]),      // input wire [10 : 0] B
  .P(temp_green)      // output wire [18 : 0] P
);

div_gen_0 temp_green_div_Gavg (
  .aclk(clk),                                      // input wire aclk
  .s_axis_divisor_tvalid(hsync_dly[2]),    // input wire s_axis_divisor_tvalid
  .s_axis_divisor_tdata(Gavg),      // input wire [7 : 0] s_axis_divisor_tdata
  .s_axis_dividend_tvalid(hsync_dly[2]),  // input wire s_axis_dividend_tvalid
  .s_axis_dividend_tdata(temp_green),    // input wire [23 : 0] s_axis_dividend_tdata
  .m_axis_dout_tvalid(),          // output wire m_axis_dout_tvalid
  .m_axis_dout_tdata(temp_green_new)            // output wire [31 : 0] m_axis_dout_tdata
);

//进行判断
always @(posedge clk or posedge rst) begin
	if (rst == 1'b1) begin
		green_new <= 'd0;
	end
	else if (temp_green_new[26:8] > 'd255) begin
		green_new <= 'd255;
	end
	else begin
		green_new <= temp_green_new[15:8];
	end
end

//blue_new
mult_gen_8xKavg bluexKavg (
  .CLK(clk),  // input wire CLK
  .A(blue),      // input wire [7 : 0] A
  .B(Kavg[10:0]),      // input wire [10 : 0] B
  .P(temp_blue)      // output wire [18 : 0] P
);

div_gen_0 temp_blue_div_Bavg (
  .aclk(clk),                                      // input wire aclk
  .s_axis_divisor_tvalid(hsync_dly[2]),    // input wire s_axis_divisor_tvalid
  .s_axis_divisor_tdata(Bavg),      // input wire [7 : 0] s_axis_divisor_tdata
  .s_axis_dividend_tvalid(hsync_dly[2]),  // input wire s_axis_dividend_tvalid
  .s_axis_dividend_tdata(temp_blue),    // input wire [23 : 0] s_axis_dividend_tdata
  .m_axis_dout_tvalid(),          // output wire m_axis_dout_tvalid
  .m_axis_dout_tdata(temp_blue_new)            // output wire [31 : 0] m_axis_dout_tdata
);

always @(posedge clk or posedge rst) begin
	if (rst == 1'b1) begin
		blue_new <= 'd0;
	end
	else if (temp_blue_new[26:8] > 'd255) begin
		blue_new <= 'd255;
	end
	else begin
		blue_new <= temp_blue_new[15:8];
	end
end

//不是数据没有对齐的问题
always @(posedge clk) begin
	fra_hsy <= temp_fra_hsy;
end

//fra_vsy,(可以随便一个)
assign fra_vsy = vsync_dly[1];


endmodule

第三部分、实现结果

1、白平衡前后对比

        没有白平衡之前,CMOS采集到的图像偏绿,白平衡之后效果就好多了👍👍👍

CMOS摄像头图像白平衡之前和白平衡之后的效果

2、总结

        这篇主要是总结了一下白平衡算法的原理的实现方法,我上面的Verilog 代码,大家只需要看明白就可以移植了。

        QQ交流群聊号码1020775171,有疑问的小伙伴可以加入哦🤗🤗🤗

        本专栏有很多我个人总结的比较好的文章,希望对你开发有帮助:FPGA的学习之旅_大屁桃的博客-CSDN博客

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

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

相关文章

使用PSIM软件生成DSP28335流水灯程序

最近在学习DSP28335芯片&#xff0c;然后在使用PSIM仿真软件时发现这个仿真软件也支持28335芯片&#xff0c;于是就想学习下如何在PSIM软件中使用DSP28335芯片。在PSIM自带的官方示例中有使用DSP28335芯片的相关例子。 工程下载链接 https://download.csdn.net/download/qq_20…

Docker Ipvlan l3s模式说明

看到Docker Ipvlan中有三种模式L2、L3、L3S模式&#xff0c;查阅了L3S&#xff0c;记录如下&#xff1a; 起因 Docker链接: IPvlan network driver 概念 注释说明&#xff08;摘选自: ipvlan-l3s模式&#xff09; L3S mode与L3 mode 的区别在于启用了iptables (conn-track…

[计算机提升] 切换(域)用户

4.14 切换(域)用户 4.14.1 为什么要切换用户 在Windows系统中&#xff0c;切换用户的主要目的是为了实现多用户共享同一台计算机的便利和安全。当多个人需要使用同一台计算机时&#xff0c;每个人可以登录自己的用户账户&#xff0c;这样可以避免互相干扰和混淆数据。 以下是…

数据可视化:普通人的信息解读法宝

在信息爆炸的时代&#xff0c;数据量庞大&#xff0c;如何高效处理和理解这些信息成为了每个人都面临的挑战。幸运的是&#xff0c;数据可视化作为一种直观的展示方式&#xff0c;已经成为提高普通人工作效率的得力工具。下面我就从可视化从业者的角度来简单聊聊这个话题。 首先…

JVM工作原理与实战(二十四):堆的垃圾回收-对象引用

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、软引用 1.软引用的执行过程 2.SoftReference队列机制 二、弱引用 三、虚引用与终结器引用 1.虚引用 2.终结器引用 总结 前言 JVM作为Java程序的运行环境&#xff0c;其负责…

【小白学机器学习3】关于最简单的线性回归,和用最小二次法评估线性回归效果, 最速下降法求函数的最小值

目录 1 什么是回归分析 1.1 什么是线性回归 1.2非线性回归 2 数据和判断方法 2.1 原始数据 2.2 判断方法&#xff1a;最小二乘法 3 关于线性回归的实测 3.1 用直线模拟 3.2 怎么判断哪个线性模拟拟合更好呢&#xff1f; 3.2.1 判断标准 3.2.2 最小二乘法 3.2.3 高维…

2024年数据中心交换机市场预测新鲜出炉,我们做了这些顺应趋势的事儿……

Dell’Oro Group发布的最新报告显示&#xff0c;2023年数据中心交换机市场与年初预测基本一致&#xff0c;200/400Gbps数据中心交换机的销售额几乎翻番。另外&#xff0c;AI&#xff08;人工智能&#xff09;与ML&#xff08;机器学习&#xff09;的发展势必推动服务器、存储、…

MATLAB字符串编辑常用代码

1.字符串赋值 % 字符串赋值 sabcdefg 2.字符串属性和操作 (1)获取字符串长度 sabcdefg;% 字符串赋值 length(a) % 获取字符串长度 (2)连接字符串 % 连接两个字符串,每个字符串最右边的空格被裁切 s1a s2b s3strcat(s1,s2) 3.字符串比较 % strcmp 比较两个字符串是…

STL---Priotity_queue+仿函数的介绍

一、优先级队列的介绍和使用 &#xff08;1&#xff09;介绍 翻译&#xff1a; &#xff08;1&#xff09; 优先队列是一种容器适配器&#xff0c;根据严格的弱排序标准&#xff0c;它的第一个元素总是它所包含的元素中最大的。 &#xff08;2&#xff09; 此上下文类似于堆&a…

VMware workstation平台下配置Fedora-Server-39-1.5虚拟机网络

VMware workstation平台下配置Fedora-Server-39-1.5虚拟机网络 Fedora包含的软件以自由及开放源码许可来发布&#xff0c;并旨在成为该技术领域的领先者。Fedora在专注创新、抢先集成新技术、与上游Linux社区紧密工作方面拥有良好名声。该文档适用于在VMware workstation平台下…

还在为iOS和iPadOS存储不足闷闷不乐?这里有优化空间的详细步骤

储存空间似乎是iPhone和iPad用户最大的抱怨之一,这要归功于应用程序占用了越来越多的储存空间,媒体变得比以往任何时候都更渴望储存。以下是解决方法。 本指南中的屏幕截图来自iPhone,但提示也适用于iPad。必要时将提及设备之间的差异。 如何检查iPhone和iPad上的储存空间…

一个好用的服务器控制面板

简介 它是一个免费开源的管理面板工具&#xff0c;可以帮助你集中管理多个服务器和网站。Ajenti 支持 Linux、BSD、Mac OS X和Windows 等多个操作系统&#xff0c;并且可以通过一个直观的 Web 界面来完成各种系统管理任务。 相比于其他管理面板&#xff0c;Ajenti有以下几个优…

【星海随笔】unix 启动问题记录.

启动Ubuntu操作系统时&#xff0c;直接进入GRUB状态。 调试时候&#xff0c;曾显示 no bootable device no known filesystem detected 注意&#xff1a; 目前 GRUB 分成 GRUB legacy 和 GRUB 2。版本号是 0.9x 以及之前的版本都称为 GRUB Legacy &#xff0c;从 1.x 开始的就称…

【用积分求抛物线与直线围成的面积】

文章目录 一、Problem Discription二、Sample Input and Sample Output三、数学分析与推导计算1. 根据抛物线顶点坐标 P 1 ( x 1 , y 1 ) P_1(x_1, y_1) P1​(x1​,y1​)以及另一个点的坐标 P 2 ( x 2 , y 2 ) P_2(x_2, y_2) P2​(x2​,y2​)&#xff0c;求出抛物线方程2. 根据…

单片机中MCU跑RTOS相比裸机的优势

经常有读者问关于RTOS的问题&#xff0c;比如&#xff1a;我现在要不要学习RTOS&#xff1f; 学习RTOS有什么好处&#xff1f; 我的项目要不要跑RTOS&#xff1f; 问这些问题&#xff0c;其实归根结底还是对RTOS理解的不够&#xff0c;项目开发的经验还不足等。针对这部分朋友…

PyTorch 内 LibTorch/TorchScript 的使用

PyTorch 内 LibTorch/TorchScript 的使用 1. .pt .pth .bin .onnx 格式1.1 模型的保存与加载到底在做什么&#xff1f;1.2 为什么要约定格式&#xff1f;1.3 格式汇总1.3.1 .pt .pth 格式1.3.2 .bin 格式1.3.3 直接保存完整模型1.3.4 .onnx 格式1.3.5 jit.trace1.3.6 jit.scrip…

品牌价值的累积与倍增:指数函数的含义及其在企业运营中的应用

品牌的价值日益凸显。品牌价值的累积与倍增不仅是企业追求的目标&#xff0c;也是市场竞争的重要标志。指数函数作为一种数学模型&#xff0c;对于描述品牌价值的增长具有重要意义。本文将深入探讨指数函数的含义及其在企业运营中的应用&#xff0c;并分析如何通过持续创新、品…

Unity 抽象工厂模式(实例详解)

文章目录 简介实例1实例2 简介 抽象工厂模式是一种创建型设计模式&#xff0c;它提供了一种方式来封装一组相关或相互依赖对象的创建过程&#xff0c;而无需指定具体类。这种模式常用于系统中有多组相关产品族&#xff0c;且客户端需要使用不同产品族中的对象时。 在Unity中&a…

canvas绘制旋转的椭圆花

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

Java 设计者模式以及与Spring关系(四) 代理模式

目录 简介: 23设计者模式以及重点模式 代理模式&#xff08;Proxy Pattern&#xff09; 静态代理示例 spring中应用 动态代理 1.基于JDK的动态代理 target.getClass().getInterfaces()作用 内名内部类写法(更简洁&#xff0c;但不推荐) 2.基于CGLIB实现 spring中应用 …