一  概述

图像处理算法一般是用matla或OpenCV实现的,若是用FPGA实现,设计思路差别极大。matlab和opencv的优势:这些工具的优势在于可以方便地载入图像文件,或输出数据到图像文件,同时提供了大量的API函数,便于使用者快速实现想要的功能,同时又能通过查看图像文件直观地看到预想结果。将算法直接在FPGA实现是有难度和漫长的,在matlab中,一个直方图处理和双边滤波器,引导图像滤波,仅仅一行代码即可,有现成的函数调用,十分简单。而在FPGA实现则需要考虑帧缓存,算法的设计结构与硬件相符合,时序等问题。很有必要对FPGA实现图像处理算法的基本思路和方法进行学习!

二  FPGA实现图像处理算法的基本思路

(一)  需求分析及问题描述

问题描述应该清楚地描述问题而不是解决方法。为使描述更具体,至少需要讨论三个方面。第一是系统功能,也就是系统需要做什么。在一个图像处理应用中,需要详细说明图像处理后的预期结果。第二,必须讨论系统的性能,即说明系统完成这些功能的指标是什么。对 千实时图像处理来说,允许的最大延时和每秒需要处理的帧数是两个很重要的指标。第三个需要考虑的方面是系统将要运行的环境。应用图像处理不仅仅包含图像处理算法,它是一个需要对整个系统进行考虑和说明的系统工程问题。其他需要 考虑的重要方面包括照明、光学及所支持的硬件和机械接口。图像处理系统之间及其与整个工程系统其他部分之间的联系也需要认真地说明和定义。比如,要做一个分辨率为640*480@60Hz的视频处理系统,要求提升每一帧图像的亮度和对比度。这就是明确的需求:即明确输入图像或视频的要求和最终的评价指标。

(二)软件算法设计及验证

软件开发及验证会适当地在硬件设计之前进行,比如在用matlab或OpenCV等工具验证算法的视觉效果和客观指标,这主要是由千在FPGA上调试算法周期过长,即使仅仅做仿真工作所消耗的时间也远远比软件多。如果在硬件上进行映射,其综合、编译和布局布线的时间花费更是无法令人接受。大部分情况下,FPGA更多的是仅仅作为一个映射工具。一般实现某个算法如红外图像细节增强,都是先花两周看经典的SCI论文,选两三篇经典的(一是权威的作者:如知名企业三星,学术机构IEEE的院士,知名大学中科院教授;二是看被引量,如60次以上,这是很多相关算法的基础,很完善成熟。三是发表时间五年之前的比较靠谱的,新的论文很少有人验证。四是看论文的实验仿真,设计是否严谨和多种指标衡量。),在对这两三篇经典的论文算法理解透了,用matlab复现。通过自己matlab仿真不同算法,从实验结果选择一种最佳的作为硬件实现。算法的精度,涉及到浮点转换为定点运算,FPGA不支持小数运算。

(三)  硬件平台设计

硬件平台的设计往往会和软件开发同时进行。通常 个算法的测试及改进是 个周期很长的任务。硬件平台的设计在算法开 发基本功能验证之后就可以对其进行整体评估。

  1. 软件与硬件的划分

硬件平台设计的第 步是合理地划分硬件和软件。 这里的硬件是指算法由 FPGA逻辑实现,软件是指算法由 DSP、ARM 或MCU软件编程实现。规则的底层图像处理操作(如形态学滤波、 Sobel 算子、均值滤波等)具有计算数据量大、 结构规则并行等特点,非常适合千用 FPGA 硬件实现。 不规则的底层图像处理操作(如具有动态可变长度循环的算法) 和串行顶层图像处理操作(如弹道计算、任务判决融合等)用 FPGA实现会非常繁琐且效率较低, 此类操作用软件实现效率较高, 开发难度较低。无论怎么划分层级,清楚地定义软件与硬件之间的接口与通信机制是基本的要求。尤其有必要设计同步和数据交换机制来促进数据流的平滑。如常见的LCD屏显示,通常是FPGA处理像素数据后通过SPI总线发送给MCU驱动显示。

2. 资源评估与FPGA选型

在硬件方案确定之后, 在确定具体的FPGA型号之前, 对整个系统所消耗的资源 进行预估是十分必要的。对千图像处理系统来讲, 比较敏感的资源是存储器资源。此外,FPGA 所拥有的些高速接口资源也是重要的考虑因素, 这主要考虑到视频处理的高带宽特点。如系统需求为2560*1600,帧率为90Hz的RGB888视频流输入,则帧缓存的带宽为2560*1600*90*24/8=1.105GB/s,而一般DDR3的带宽为800M,需要两片DDR3才行。

(四)   FPGA映射

FPGA映射是将软件算法转换为FPGA设计的过程。这个在书籍<<基于FPGA的数字图像处理原理及应用>>的第四章,有讲解到映射技术,如下图所示,需要时可以看书籍进一步了解

(五)  仿真验证

在FPGA映射之后,接下来的重点工作是对设计的系统进行仿真和验证。在FPGA代码撰写完毕时对其进行功能测试是十分有必要的。一般是搭建视频的modelsim仿真平台,即编写一个Verilog文件模拟符合标准视频时序的视频输入源,提供给我们的设计模块,仿真观察波形,将算法处理的结果用txt文本存储,再用matlab观察对比效果。本文的后面会介绍一个vga的模拟输入。在硬件中的在线调试也是十分必要的。最简单的方法是将主信号布线到不用的IO,口上, 使得它们从FPGA外部是可观测的, 在外部使用 个示波器或是逻辑分析仪来监控信号。 此外, Xlinx和Altera厂商提供的IDE中也提供了虚拟的逻辑分析仪来辅助调试。 不过, 辅助调试手段需要占用片内的存储器资源。

三  VGA

上面介绍图像处理算法的fpga实现的基本思路,现在通过搭建一个vga时序来实现搭建图像仿真平台的第一步。vga时序模拟图像算法的视频输入,第二步就是设计图像处理算法,如直方图统计。第三步把算法对图像数据的处理结果用matlab直观显示,也可以和matlab实现算法的处理结果对比。通过这三步就很直观显示图像输入输出的效果,对于验证图像算法的有效性很方便。

(一) 外部接口

1.VGA原理图及端口

上面是vga接口的电路图,可以看出总共五个信号,分成两类,一是控制VGA驱动的行同步信号VGA_HS(HSync) 和场同步信号(VSync);二是控制像素数据输出的RGB信号,根据vga接口和RGB的组合,常见RGB格式有RGB888和RGB565.

2. VGA扫描方式

显示是用逐行扫描的方式解决,阴极射线枪发出电子束打在涂有荧光粉的荧光屏上,产生RGB 三基色,合成一个彩色像素。扫描从屏幕的左上方开始,从左到右,从上到下,进行扫描,每扫完一行,电子束回到屏幕的左边下一行的起始位置,在这其间CRT 对电子束进行消隐。每行结束时,用行同步信号进行同步;扫描完所有行,用场同步信号进行同步,并使扫描回到屏幕左上方,同时进行场消隐,预备下一场的扫描。

(二)  640*480@60Hz的VGA实现

1.VESA显示标准

下面我们以实现640*480@60Hz的vga驱动来掌握VGA的实现参数和驱动。对于vga的驱动,首先有一个官方的标准,VESA视频标准显示手册,这是做视频显示的权威指南,下面我们给出这个分辨率的vesa标准:

首先是红色方框的图像有效显示的分辨率和帧数(一秒内显示的图像数),即640*480为有效区域和屏幕的总分辨率800*525区别,大致理解有效区域为总屏幕的一部分,这是理解重点,牵涉到后面的像素有效信号test_dvalid(de)的理解。像素的时钟由分辨率和帧率决定,pixel clock = (1/帧数)/屏幕分辨率(800*525)=25M左右

2. VGA屏幕一帧的时序参数图

其他的时序参数如行场同步,场前肩,场后肩,行左边界,行右边界。要结合下面的两张图才能深刻理解,光看上面的vesa标准还是很困惑这些时序参数的含义。

上面的第二张为从<VESA Display Monitor Timing Standard>手册中截取的屏幕一帧的时序参数图,从图中可知红框表示图像的有效区域640*480对应着它上面图片的图像显示区。绿框为整个显示屏幕,分辨率大小800*525为标准手册的Hor Total Time=800 pixels和Ver Total Time = 525 lines,分别表示总行时间,总列时间。分别以像素时钟的周期和行的时间为单位。这个分辨率对应着它上面图2的整个屏幕的大小800*525.故后面代码中的像素有效标志信号test_dvalid为图像像素在红框的有效区域时,行同步信号有效的值,表示输出的像素有效。

时序参数可以参考上面的解释。

3. VGA 显示的时序图

(三) VGA时序模拟的设计思路及代码

1.设计思路

上面是vga行场扫描的时序图,通过这个时序图结合我们上面的分析,就能编写Verilog代码驱动vga显示。行同步信号(Hsync)与场同步信号都可以每行/帧一开始拉高/低,场同步信号为每一帧的起始点。行同步信号则为每一行像素的起始点。设计思路,结合手册,先编写行计数器cnt_h和垂直计数器cnt_v,分别是计数到800-1,525-1,垂直计数器cnt_v是以每行计数完加1的。接着是像素有效信号test_dvalid,它是在有效区域640*480内,在像素时钟的驱动下拉高。场同步信号test_vsync_temp根据时序参数知为2lines的高电平,故在垂直计数器cnt_v在0-2范围则拉高,其余时间为零。故场同步信号表示每一帧的开始,即新帧的到来,它满足每一秒60帧,即场同步信号的周期为1/60秒,约为16.6毫秒,故可判读时序是否正确。

2. 代码

  1 `timescale 1 ns/1 ns
2
3 `define SEEK_SET 0
4 `define SEEK_CUR 1
5 `define SEEK_END 2
6 //the simulation of image conform to the VESA stand
7 //so modified the parameters
8 //Timing Name = 640 x 480 @ 60Hz;
9 //acquired from vesa
10 module ima_src(
11 reset_l,
12 clk,
13 src_sel,
14 test_vsync,
15 test_dvalid,//pixel valid
16 test_data,
17 clk_out
18 );
19
20 parameter iw = 640;//640*2 2 bytes per pixels
21 parameter ih = 480;//512 plus one command line
22 parameter dw = 8;
23
24 parameter h_total = 800;//Hor Total Time = 800 pixel
25 parameter v_total = 525 ;//Ver Total Time = 16.683; // (msec) = 525 lines
26
27 parameter sync_b = 2;//V Front Porch/ Ver Sync Time=2 lines
28 parameter sync_e = 2;//Ver Sync
29 parameter vld_b = (2+25+8);//V Back Porch=35
30 //Ver Addr Time =480 lines
31 parameter h_b = (96+40+8);//Hor Sync Time(96)+H Back Porch(40)+H Left Border(8) =144
32 input reset_l,clk;//clock,reset
33 input [3:0]src_sel;//to select the input file
34 output test_vsync,test_dvalid,clk_out;
35 output [dw-1:0]test_data;
36
37 reg [dw-1:0]test_data_reg;
38 reg test_vsync_temp;
39 reg test_dvalid_tmp;
40 reg [1:0]test_dvalid_r;
41
42 reg [10:0] h_cnt;
43 reg [10:0] v_cnt;
44
45 integer fp_r;
46 integer cnt=0;
47
48 assign clk_out = clk;//output the dv clk
49
50 assign test_data = test_data_reg;//test data output
51
52 //read data from file
53
54 always @(posedge clk or posedge test_vsync_temp )
55
56 if ((((~test_vsync_temp))) == 1'b0)
57 cnt<=0;//clear file pointer when a new frame comes
58 else
59 begin
60 if (test_dvalid_tmp == 1'b1)
61 begin
62 case (src_sel)
63 4'b0000 :fp_r = $fopen("../poc/ln.txt","r");
64 4'b0001 :fp_r = $fopen("../poc/ln.txt","r");//very error
65 4'b0010 :fp_r = $fopen("../poc/recovery/e_640x480_hex.txt","r");
66 4'b0011 :fp_r = $fopen("txt_source/test_src3.txt", "r");
67 4'b0100 :fp_r = $fopen("txt_source/test_src4.txt", "r");
68 4'b0101 :fp_r = $fopen("txt_source/test_src5.txt", "r");
69 4'b0110 :fp_r = $fopen("txt_source/test_src6.txt", "r");
70 4'b0111 :fp_r = $fopen("txt_source/test_src7.txt", "r");
71 4'b1000 :fp_r = $fopen("txt_source/test_src8.txt", "r");
72 4'b1001 :fp_r = $fopen("txt_source/test_src9.txt", "r");
73 4'b1010 :fp_r = $fopen("txt_source/test_src10.txt", "r");
74 4'b1011 :fp_r = $fopen("txt_source/test_src11.txt", "r");
75 4'b1100 :fp_r = $fopen("txt_source/test_src12.txt", "r");
76 4'b1101 :fp_r = $fopen("txt_source/test_src13.txt", "r");
77 4'b1110 :fp_r = $fopen("txt_source/test_src14.txt", "r");
78 4'b1111 :fp_r = $fopen("txt_source/test_src15.txt", "r");
79 default :fp_r = $fopen("../poc/ln.txt","r");
80 endcase
81
82 $fseek(fp_r,cnt,0);
83 $fscanf(fp_r, "%02x\n", test_data_reg);
84 cnt <= cnt + 4 ;
85 $fclose(fp_r);
86 //$display("%02x",test_data_reg); //for debug use
87 end
88 end
89
90 //horizon counter
91 always @(posedge clk or posedge reset_l)
92 if (((~(reset_l))) == 1'b1)
93 h_cnt <= #1 {11{1'b0}};
94 else
95 begin
96 if (h_cnt == ((h_total - 1)))
97 h_cnt <= #1 {11{1'b0}};
98 else
99 h_cnt <= #1 h_cnt + 11'b00000000001;
100 end
101
102 //vertical counter
103 always @(posedge clk or posedge reset_l)
104 if (((~(reset_l))) == 1'b1)
105 v_cnt <= #1 {11{1'b0}};
106 else
107 begin
108 if (h_cnt == ((h_total - 1)))
109 begin
110 if (v_cnt == ((v_total - 1)))
111 v_cnt <= #1 {11{1'b0}};
112 else
113 v_cnt <= #1 v_cnt + 11'b00000000001;
114 end
115 end
116
117 //field sync
118 always @(posedge clk or posedge reset_l)
119 if (((~(reset_l))) == 1'b1)
120 test_vsync_temp <= #1 1'b1;
121 else
122 begin
123 if (v_cnt >= 0 & v_cnt < (sync_b ))
124 test_vsync_temp <= #1 1'b1;
125 else
126 test_vsync_temp <= #1 1'b0;
127 end
128
129 assign test_vsync = (test_vsync_temp);
130
131 //horizon sync
132 always @(posedge clk or posedge reset_l)
133 if (((~(reset_l))) == 1'b1)
134 test_dvalid_tmp <= #1 1'b0;
135 else
136 begin
137 if (v_cnt >= vld_b & v_cnt < ((vld_b + ih)-1))
138 begin
139 if (h_cnt >= h_b & h_cnt < ((h_b + iw)-1))
140 test_dvalid_tmp <= #1 1'b1;
141 else
142 test_dvalid_tmp <= #1 1'b0;
143 end
144 end
145 assign test_dvalid = test_dvalid_tmp;
146
147 always @(posedge clk or posedge reset_l)
148 if (((~(reset_l))) == 1'b1)
149 test_dvalid_r <= #1 2'b00;
150 else
151 test_dvalid_r <= #1 ({test_dvalid_r[0], test_dvalid_tmp});
152
153 endmodule

明显的差别是数据的输出,这次的设计,通过文件操作来获得图像的数据,为了和matlab联合仿真。在硬件描述语言仿真平台中简单地载入图像文件和输出图像文件,那么对图像类处理的仿真将会带来极大的方便。方式通过用matlab把图像转换为txt文本,用Verilog的$fopen,$fclose,$fscanf,$fread,$fwrite等进行文件操作,注意点是只能用来仿真,不可综合。后面Verilog算法处理后存储为txt文本还能用来作为matlab的图像输入进行显示。这样很方便能验证算法的处理效果,不要很麻烦到硬件平台去观察。

3.MATLAB代码

通过matlab代码将图像转换为8比特的16进制形式作为设计的输入数据。MATLAB处理结果如下:

matlab功能:把640*480的lean原图转换640*480=307200个8比特的像素数据,存储为txt文本。后面modelsim仿真,打开这个文件作为vga模拟视频源的输入数据。

clc;
clear; %% 数据获取
RGB = imread('lean640_480.bmp'); %rgb原始图像
GRAY = rgb2gray(RGB); %Matlab变换灰度图像 fid = fopen('./lean640_480_hex.txt','wt');
for i = 1:size(RGB,1)
for j = 1:size(RGB,2)
fprintf(fid,'%2x\n',RGB(i,j));%每个数据之间用空格分开
end
end %% 画图显示
figure(1);
subplot(1,3,1);
imshow(RGB);
title('lena原始图像'); subplot(1,3,2);
imshow(GRAY);
title('Matlab变换灰度图像');

  再附上lean原图640*480如下:供图像处理算法使用,这是图像领域的经典。

(四) 仿真

1.testbench代码设计

 1 `timescale 1ns/1ps
2
3 module tb_top;
4
5 //========================================================
6 //parameters
7 parameter CLK_FREQ = 25.200;//ddr reference clock frequency, unit: MHz
8 parameter CLK_PERIOD = 1000.0/CLK_FREQ; //unit: ns
9 //pixel_total=h_total*v_total*60=25_200_000
10 // parameter FREQ = 100_000_000 ;
11 parameter sim_num = 5_000_000 ;
12 // parameter BAUDRATE = 115200 ;
13 //ima parameter
14 parameter iw = 640;//640*2 2 bytes per pixels
15 parameter ih = 480;//512 plus one command line
16 parameter dw = 8;
17
18 parameter h_total = 800;//Hor Total Time = 800 pixel
19 parameter v_total = 525 ;//Ver Total Time = 16.683; // (msec) = 525 lines
20
21 parameter sync_b = 2;//V Front Porch
22 parameter sync_e = 2;//Ver Sync
23 parameter vld_b = 25+8;//V Back Porch+V top Borch
24
25 //=======================================================
26 reg clk; // 50M
27 reg rst_n ;
28 reg [3:0] de ;
29
30
31 wire test_vsync;
32 wire test_dvalid;
33 wire[7:0] test_data;
34 wire clk_out;
35
36
37 //========================================================
38 GSR GSR(.GSRI(1'b1));
39
40
41 initial
42 begin
43 rst_n = 1'b0;
44 de =4'd0;
45 #200;
46 de = 4'd1;
47 rst_n = 1'b1;
48
49
50
51 end
52
53 //----------------------------------------------------
54 //ref clk
55 initial
56 begin
57 clk = 1'b0;
58 end
59
60 always #(CLK_PERIOD/2.0) clk = ~clk;
61
62 //==================================================
63 //ima_src
64 //parameter must be connected to constant
65 ima_src inst_ima_src (
66 .reset_l (rst_n),
67 .clk (clk),
68 .src_sel (de),
69 .test_vsync (test_vsync),
70 .test_dvalid (test_dvalid),
71 .test_data (test_data),
72 .clk_out (clk_out)
73 );
74
75 endmodule

test Code

2.波形

从仿真波形看出一帧的时间为16.6ms左右,即场同步信号的周期, 符合每秒60帧的时序。并且像素数据在像素有效信号拉高是输出。

四  总结

一是至少看三种以上的参考资料。如本次学习搭建图像仿真平台,就是<<基于FPGA的数字图像处理原理及应用>>,然后是网上博客如咸鱼FPGA;V3学院就业办的基础课程第17讲VGA,OpensLee, 开源骚客,vesa标准手册等。

  二是,循序渐进搭仿真。如刚开始是直接给时钟复位,让仿真跑起来。其次是理解别人的设计逻辑。这时候通过上面的各种资料,这一步可能花两三天,最后就是修改逻辑和不断仿真调试,直到符合vesa时序图。
  三是,在实践过程中记录。遇到问题及解决过程。

参考资料:

1.<<基于FPGA的数字图像处理原理及应用>>

2.V3学院--第十七讲、VGA 接口驱动

3. vesa标准手册

4.https://www.cnblogs.com/huangwei0521/

图像处理算法的仿真平台之VGA时序的更多相关文章

  1. Matlab/Modelsim图像联合仿真平台

    FPGA图像仿真平台 1 引言 在使用modelsim进行图像算法的功能仿真时,无法得到图像的实时预览,因此直观性有所欠缺.因此可配合matlab使用,通过modelsim读出txt格式的图像,利用m ...

  2. 【转载】VGA时序与原理

    显示器扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信 ...

  3. Modelsim的自动化脚本仿真平台

    自动化仿真平台由tcl语言搭建,大规模设计使用此平台让仿真便捷不少.大体上用tcl语言进行modelsim仿真的流程如下: 1. 建立库 2. 映射库到物理目录 3. 编译源代码 4. 启动仿真器 5 ...

  4. P2P/WSN信任建模与仿真平台

    1.ART Testbed 该平台是基于多代理的信任仿真平台,官网的介绍如下: The Agent Reputation and Trust (ART) Testbed initiative has ...

  5. scriptol图像处理算法

    神奇的图像处理算法   相似图片搜索是利用数学算法,进行高难度图像处理的一个例子.事实上,图像处理的数学算法,已经发展到令人叹为观止的地步. Scriptol列出了几种神奇的图像处理算法,让我们一起来 ...

  6. 记录我第一次在Android开发图像处理算法的经历

    大概是四月底的时候.有人加我QQ问我是否做能做一些基于图像皮肤检測的算法, 主要是实现对皮肤六项指标: 1.      水分 2.      有份 3.      痤疮与痘痘 4.      色斑与肤 ...

  7. 利用IT++搭建通信仿真平台

    IT++ is a C++ library of mathematical, signal processing and communication classes and functions.也就是 ...

  8. 开源自动驾驶仿真平台 AirSim (1) - Unreal Engine

    AirSim 官方Github: https://github.com/Microsoft/AirSim AirSim 是微软的开源自动驾驶仿真平台(其实它还能做很多事情,这里主要用于自动驾驶仿真研究 ...

  9. VGA 时序标准

    VGA 显示器扫描方式从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT 对电子束进行消隐,每行结束时,用行同步信号进行同步:当扫描完所有的行 ...

随机推荐

  1. Jmeter(五十一) - 从入门到精通高级篇 - jmeter之运动战(详解教程)

    1.简介 运动战是一种军事作战方式,依托较大的作战空间来换取时间移动兵力包围敌方,以优势兵力速战速决,运动战的运用归为这样一段话"避敌主力,诱敌深入,集中优势兵力逐个击破".今天宏 ...

  2. 容斥+dp (一)

    ARC115 E AtCoder Problem Statement Given is a sequence of \(N\) integers \(A_1\),\(A_2\),...,\(A_N\) ...

  3. STS或eclipse中导入新项目出现红色感叹号红色叉叉的问题

    maven项目 原因: jar包缺失 没有正确配置Maven仓库 解决: Window->Preferences->Maven->Installations->Add 添加你的 ...

  4. 「题解」300iq Contest 2 B Bitwise Xor

    本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:gym102331B. 题意概述 给你一个长度为 \(n\) 的序列 \(a_i\),求一个最长的子序列满足所有子序列中的 ...

  5. 「题解」NWRRC2017 Joker

    本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:洛谷 P7028.gym101612J. 题意概述 有一个长度为 \(n\) 的数列,第 \(i\) 个元素的值为 \(a ...

  6. .NET平台系列27:在 Linux 上安装 .NET Core/.NET5/.NET6

    系列目录     [已更新最新开发文章,点击查看详细] .NET 在不同的 Linux 发行版上可用. 大多数 Linux 平台和发行版每年都有一个主要版本,并提供用于安装 .NET 的包管理器. 本 ...

  7. 【NX二次开发】创建扫描特征

    扫描特征相关函数: 创建扫描特征的相关函数: 1.UF_MODL_create_extrude_trim_opts 拉伸 2.UF_MODL_create_extrude_trim_opts1 拉伸 ...

  8. 一起来踩踩 Spring 中这个循环依赖的坑

    1. 前言 2. 典型场景 3. 什么是依赖 4. 什么是依赖调解 5. 为什么要依赖注入 6. Spring的依赖注入模型 7. 非典型问题 参考资料 1. 前言 这两天工作遇到了一个挺有意思的Sp ...

  9. noip2006 总结

    T1 能量项链 原题 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子,前一颗珠子 ...

  10. 小程序动态修改页面标题setNavigationBarTitle

    可以使用setNavigationBarTitle方法动态设置页面标题 wx.setNavigationBarTitle({ title: options.name, })