imclearboder matlab,Lucas

Lucas-Kanade跟蹤算法是視覺跟蹤中一個很經典的基于點的逐幀跟蹤算法。起初這個算法是用來求解stero matching1的,后來經過Carlo Tomasi2和Jianbo Shi3等人的發展漸趨成熟。Jianbo Shi提出了一種篩選跟蹤點特征的方法,使得特征的跟蹤更可靠。Jean-Yves Bouguet4詳細闡述了如何采用金字塔方式實現LK算法以處理兩幀之間特征點位移較大的情況。

問題闡述

首先我們來看一下我們要解決的問題是什么?LK算法是基于特征點的跟蹤,而這里的特征點就是每個點對應的一個小窗口圖像塊,LK所要解決的是求解連續兩幀圖像相同特征點的位移問題。這里我們假設I和J為連續兩幀圖像,其(x,y)點的灰度值分別對應I(x,y),J(x,y)。設u=[ux,uy]T是圖像I上一點,LK算法的目標是在圖像J找到一點v=u+d=[ux+dx,uy+dy]T使得點I(u)和點J(v)是同一個位置。為了求解這樣的點,LK求解這兩個點對應的小窗口內像素的相似度。設ωx和ωy分別是點左右擴展的窗口范圍,這樣我們可以定義如下residual function為

(d)=(dx,dy)=∑x=uxωxux+ωx∑y=uyωyuy+ωy(I(x,y)J(x+dx,y+dy))2

窗口大小為(2ωx+1)×(2ωy+1),通常情況下ωx和ωy的值為2,3,4,5,6,7。

標準LK算法

針對上述最優化問題,求解方法是求解(d)關于向量d的偏導使其等于0,即

(d)d|d=dopt=[00]

這樣可以推到出其偏導結果:

(d)d=2∑x=uxωxux+ωx∑y=uyωyuy+ωy(I(x,y)J(x+dx,y+dy))J(x+dx,y+dy)d=2∑x=uxωxux+ωx∑y=uyωyuy+ωy(I(x,y)J(x+dx,y+dy))[JxJy]

利用泰勒級數展開J(x+dx,y+dy)得,

J(x+dx,y+dy)=J(x,y)+(dxx+dyy)J(x,y)+12!(dxx+dyy)2J(x,y)+...≈J(x,y)+(dxx+dyy)J(x,y)≈J(x,y)+[JxJy]d

這樣得出residual function為,

(d)d≈2∑x=uxωxux+ωx∑y=uyωyuy+ωy(I(x,y)J(x,y)[JxJy]d)[JxJy]

這里關于圖像J(x,y)的偏導可以通過求解I(x,y)的偏導近似計算。設

▽I=[IxIy]=[JxJy]T,

δI=I(x,y)J(x,y) 這樣residual function變為

12(d)d≈∑x=uxωxux+ωx∑y=uyωyuy+ωy(▽ITdδI)▽IT≈∑x=uxωxux+ωx∑y=uyωyuy+ωy(▽ITd▽ITδI▽IT)

等式兩邊取倒置

12[(d)d]T≈∑x=uxωxux+ωx∑y=uyωyuy+ωy(▽IdT▽IδI▽I)≈∑x=uxωxux+ωx∑y=uyωyuy+ωy(▽I▽ITdδI▽I)≈∑x=uxωxux+ωx∑y=uyωyuy+ωy([I2xIxIyIxIyI2y]d[δIIxδIIy])

我們用簡單符號替代其中的兩個部分,分別設

Gb=∑x=uxωxux+ωx∑y=uyωyuy+ωy[I2xIxIyIxIyI2y]=∑x=uxωxux+ωx∑y=uyωyuy+ωy[δIIxδIIy]

現在residual function變成了,

12[(d)d]T≈Gdb

使上式等于0,得出位移d為,

d=G1b

這里必須保證G是可逆的,也就是保證圖像I(x,y)在x和y方向上的梯度值必須不是0。

以上便是基本的LK算法的推導過程,具體實現的時候需要多次的迭代才能得到一個較準確的點的位移矢量,類似牛頓-拉弗森方法(Newton-Raphson method)的迭代過程,這是一個逐漸趨近最優值的過程。下面詳細介紹迭代的過程,針對第k(k1)次迭代:

設第k1次迭代的位移dk1=[dk1x,dk1y],則我們利用第k1次迭代的位移作為第k次迭代位移的初始化值,即當前次迭代的J(x,y)變為

J(x,y)=J(x+dk1x,y+dk1y)

residual function變為

(d)=(dx,dy)=∑x=uxωxux+ωx∑y=uyωyuy+ωy(I(x,y)J(x+dkx,y+dky))2

通過一次標準的LK算法,得出第k次的位移

dk=G1bk

這里我們發現每次迭代中,G是不變的,通過I(x,y)計算,唯一變化的是b,每次迭代圖像J(x,y)對應的窗口都會向所要求的位置點靠近一點點(即上一次迭代的位移作為初始化),而b的計算與J(x,y)有關,所以每次迭代都會發生變化,這樣每次迭代需要計算的就只有b。

假設進行了K次迭代后收斂,最終位移的結果為

d=∑k=1Kdk

對于第一次迭代其對應的初始化位移為:

d0=[00]T

但是上述推導的一個基本假設是點特征的位移是很小的,這樣才能滿足泰勒展開式中只保留前兩項的近似操作。而為了能處理較大的位移情況,則需要基于圖像金字塔在不同分辨率的圖層下進行跟蹤。

圖像金字塔跟蹤

首先舉一個簡單的例子,比如知道一個點前后兩幅圖像的位移為16個像素,這么大的位移直接使用標準LK算法是很難計算出來結果的,而如果在圖像分辨率降低到原來一半后,其位移就變為8個像素,再降低一半,則為4個像素,如果金字塔的層數是3,則在最底層,點的位移只有兩個像素,這樣就滿足了小位移的假設。這樣首先在最底層進行標準LK算法,得出一個位移后乘以2作為上一層的初始位移,再進行標準LK算法,以此類推,最終得到點的位移。

設圖像金字塔層數為L=0,1,2...Lm,跟蹤是從圖像金字塔的最底層Lm開始的,對于圖像從第L+1層到L層的跟蹤流程,和標準LK算法的迭代有點類似,第L層的初始化位置是基于第L+1層計算出來的。

設gL=[gLxgLy]T是第L層的初始化位移,它是通過第L+1層的位移計算得到的。這樣第L層的residual function就變成了

L(dL)=(dLx,dLy)=∑x=uLxωxuLx+ωx∑x=uLyωyuLy+ωy(IL(x,y)JL(x+gLx+dLx,y+gLy+dLy))2

從上式可以看出,第L層的JL(x,y)由于有了gL作為初始化位置使得要求解的位移dL變得很小,也就很適合用標準的LK算法計算了。

gL的計算是通過第L+1層的位移和初始化位置計算的,

gL=2(gL+1+dL+1)

對于最底層的初始化位置設為,

gLm=[00]T

最后得出點的位移為,

d0=g0+d0

圖像金字塔的構建是通過首先對上一層圖像進行去邊緣濾波,然后下采樣得到的,具體的實現參考下面章節。

OpenCV代碼實現分析

這個算法的實現主要分為三個重要的部分:圖像金字塔的構建,圖像梯度圖的計算,標準LK算法的迭代。

圖像金字塔的構建

OpenCV是通過下采樣和去邊緣濾波器兩個流程生成的多分辨率的圖像金字塔,當然為了程序的優化,這兩個流程是同時進行的。濾波器采用的kernel是

12561464141624164624362464162416414641

程序實現的核心函數是

1void pyrDown(InputArray src, OutputArray dst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT )

其OpenCV的源代碼如下(OpenCV-2.4.8/video/pyramids.cpp/line:187)。這個函數實現的大體思路是以目標圖像的長寬為基準同時實現對源圖像的去邊緣濾波以及下采樣操作。因為圖像在濾波后還要做下采樣,如果這兩步驟是分開做的話,前面濾波到的像素就會額外計算了一半下采樣后根本不需要的像素,浪費了計算,所以這里僅僅是濾波了下采樣中保留下來的像素。

濾波器的實現最直接的想法就是每次計算所有窗口里面的像素值然后求均值,但這樣做會重復計算前后兩行重疊的部分像素和,代碼效率并不高,實際上代碼中的實現是首先計算每一個像素對應的前后兩行的行和,然后存儲下這5個行和,每行所有像素計算完所有5個行和并存儲好后,再用一個for循環求和每個像素的5個行和,這樣就可以避免重復計算前后兩行重復的行和而提高了效率。如下圖所示,中間紅色像素對應窗口為1-5行,藍色像素對應窗口為2-6行,其中其中2-5行的和都是重復的,不需要重復計算。

61147728_1.pngpyrDown實現示意圖

這里有幾個需要解釋的地方:

第一,代碼第54行的for循環實現的是求解每個元素對應的各個行和。這里它采用了一種循環存取機制。例如,當計算目標圖像第k行像素(即源圖像第2k行像素)時,其需要求解的行和分別是對應源圖像上的2k-2,2k-1,2k,2k+1,2k+2行,這時假設內存中是按照順序方式存儲的,當計算目標圖像k+1行像素(即源圖像第2k+2行像素)時,我們只需要再計算2k+3和2k+4的行和并且存儲在本來存儲2k-2和2k-1行的行和的內存中,這樣的計算和存儲開銷是最小的。這樣說可能有點抽象,如下圖所示,左邊是計算目標圖像第k行像素時,數組中5個元素存儲的內容,右邊是計算目標圖像k+1行像素存儲的內容,僅僅是把原來存儲2k-2和2k-1行的元素替換成新計算出來的2k+3和2k+4行的行和,這樣在訪問這些行和時,順序就會發生一定的變化,由左邊的1,2,3,4,5變成右邊的4,5,1,2,3。這樣在計算k+2行像素時,只需把原來存儲2k-2和2k-1的內存替換為2k+3和2k+4的行和即可,然后依次類推。查看程序第57行WT* row = buf + ((sy - sy0) % PD_SZ)*bufstep;,采用的就是這種循環存儲方法。那這樣在取這些存在數組中的行和是,順序也是對應的順序,因為每個行和要乘以的權重不一,自然順序不能錯。順序的計算方法查看程序第122行rows[k] = buf + ((y*2 - PD_SZ/2 + k - sy0) % PD_SZ)*bufstep;

61147728_2.png存儲示意圖

第二,程序在處理邊緣問題是采用了borderInterpolate這個函數,主要涉及對稱邊緣圖像或直接復制邊緣圖像等方式添加虛擬邊緣。當boderType = BORDER_REPLICATE時,是簡單的復制邊緣圖像,當boderType = BORDER_REFLECT時,就是以邊緣為中心對稱復制邊緣里層的圖像。而pyrDown采用的就是這種邊緣處理方式。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130template void

pyrDown_( const Mat& _src, Mat& _dst, int borderType )

{

//濾波器的窗口大小

const int PD_SZ = 5;

typedef typename CastOp::type1 WT;

typedef typename CastOp::rtype T;

CV_Assert( !_src.empty() );

Size ssize = _src.size(), dsize = _dst.size();

int cn = _src.channels();

int bufstep = (int)alignSize(dsize.width*cn, 16);

AutoBuffer _buf(bufstep*PD_SZ + 16);

WT* buf = alignPtr((WT*)_buf, 16);

//處理左右邊界

int tabL[CV_CN_MAX*(PD_SZ+2)], tabR[CV_CN_MAX*(PD_SZ+2)];

AutoBuffer _tabM(dsize.width*cn);

int* tabM = _tabM;

//存儲PD_SZ行的和,用于第二次循環列的求和

WT* rows[PD_SZ];

CastOp castOp;

VecOp vecOp;

CV_Assert( std::abs(dsize.width*2 - ssize.width) <= 2 &&

std::abs(dsize.height*2 - ssize.height) <= 2 );

int k, x, sy0 = -PD_SZ/2, sy = sy0, width0 = std::min((ssize.width-PD_SZ/2-1)/2 + 1, dsize.width);

for( x = 0; x <= PD_SZ+1; x++ )

{

int sx0 = borderInterpolate(x - PD_SZ/2, ssize.width, borderType)*cn;

int sx1 = borderInterpolate(x + width0*2 - PD_SZ/2, ssize.width, borderType)*cn;

for( k = 0; k < cn; k++ )

{

tabL[x*cn + k] = sx0 + k;

tabR[x*cn + k] = sx1 + k;

}

}

ssize.width *= cn;

dsize.width *= cn;

width0 *= cn;

for( x = 0; x < dsize.width; x++ )

tabM[x] = (x/cn)*2*cn + x % cn;

//基于目標圖像的高度

for( int y = 0; y < dsize.height; y++ )

{

T* dst = (T*)(_dst.data + _dst.step*y);

WT *row0, *row1, *row2, *row3, *row4;

// fill the ring buffer (horizontal convolution and decimation)

//水平方向求解各個行和

for( ; sy <= y*2 + 2; sy++ )

{

//循環存儲行和

WT* row = buf + ((sy - sy0) % PD_SZ)*bufstep;

int _sy = borderInterpolate(sy, ssize.height, borderType);

const T* src = (const T*)(_src.data + _src.step*_sy);

int limit = cn;

const int* tab = tabL;

for( x = 0;;)

{

for( ; x < limit; x++ )

{

row[x] = src[tab[x+cn*2]]*6 + (src[tab[x+cn]] + src[tab[x+cn*3]])*4 +

src[tab[x]] + src[tab[x+cn*4]];

}

if( x == dsize.width )

break;

if( cn == 1 )

{

for( ; x < width0; x++ )

row[x] = src[x*2]*6 + (src[x*2 - 1] + src[x*2 + 1])*4 +

src[x*2 - 2] + src[x*2 + 2];

}

else if( cn == 3 )

{

for( ; x < width0; x += 3 )

{

const T* s = src + x*2;

WT t0 = s[0]*6 + (s[-3] + s[3])*4 + s[-6] + s[6];

WT t1 = s[1]*6 + (s[-2] + s[4])*4 + s[-5] + s[7];

WT t2 = s[2]*6 + (s[-1] + s[5])*4 + s[-4] + s[8];

row[x] = t0; row[x+1] = t1; row[x+2] = t2;

}

}

else if( cn == 4 )

{

for( ; x < width0; x += 4 )

{

const T* s = src + x*2;

WT t0 = s[0]*6 + (s[-4] + s[4])*4 + s[-8] + s[8];

WT t1 = s[1]*6 + (s[-3] + s[5])*4 + s[-7] + s[9];

row[x] = t0; row[x+1] = t1;

t0 = s[2]*6 + (s[-2] + s[6])*4 + s[-6] + s[10];

t1 = s[3]*6 + (s[-1] + s[7])*4 + s[-5] + s[11];

row[x+2] = t0; row[x+3] = t1;

}

}

else

{

for( ; x < width0; x++ )

{

int sx = tabM[x];

row[x] = src[sx]*6 + (src[sx - cn] + src[sx + cn])*4 +

src[sx - cn*2] + src[sx + cn*2];

}

}

limit = dsize.width;

tab = tabR - x;

}

}

// do vertical convolution and decimation and write the result to the destination image

//循環取得各個行和的指針

for( k = 0; k < PD_SZ; k++ )

rows[k] = buf + ((y*2 - PD_SZ/2 + k - sy0) % PD_SZ)*bufstep;

row0 = rows[0]; row1 = rows[1]; row2 = rows[2]; row3 = rows[3]; row4 = rows[4];

x = vecOp(rows, dst, (int)_dst.step, dsize.width);

//每一行結束的位置,把該行所有像素點的濾波結果求解出來

for( ; x < dsize.width; x++ )

dst[x] = castOp(row2[x]*6 + (row1[x] + row3[x])*4 + row0[x] + row4[x]);

}

}

圖像梯度圖的計算

圖像梯度的實現也要設計到窗口計算問題,所以和上面提到的方法有類似的地方,也是先計算行和,再計算列和。程序采用了Sharr算子進行梯度的計算,x方向的梯度圖算子是

31030003103 y方向的梯度算子是

30310010303

OpenCV詳細代碼如下,注意這里程序計算出的x和y方向的梯度值分別存放在了一個雙通道的目標圖像中,每個通道占用一個方向的梯度值。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86static void calcSharrDeriv(const cv::Mat& src, cv::Mat& dst)

{

using namespace cv;

using cv::detail::deriv_type;

int rows = src.rows, cols = src.cols, cn = src.channels(), colsn = cols*cn, depth = src.depth();

CV_Assert(depth == CV_8U);

dst.create(rows, cols, CV_MAKETYPE(DataType::depth, cn*2));

#ifdef HAVE_TEGRA_OPTIMIZATION

if (tegra::calcSharrDeriv(src, dst))

return;

#endif

int x, y, delta = (int)alignSize((cols + 2)*cn, 16);

AutoBuffer _tempBuf(delta*2 + 64);

deriv_type *trow0 = alignPtr(_tempBuf + cn, 16), *trow1 = alignPtr(trow0 + delta, 16);

#if CV_SSE2

__m128i z = _mm_setzero_si128(), c3 = _mm_set1_epi16(3), c10 = _mm_set1_epi16(10);

#endif

for( y = 0; y < rows; y++ )

{

const uchar* srow0 = src.ptr(y > 0 ? y-1 : rows > 1 ? 1 : 0);

const uchar* srow1 = src.ptr(y);

const uchar* srow2 = src.ptr(y < rows-1 ? y+1 : rows > 1 ? rows-2 : 0);

deriv_type* drow = dst.ptr(y);

// do vertical convolution

x = 0;

#if CV_SSE2

for( ; x <= colsn - 8; x += 8 )

{

__m128i s0 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow0 + x)), z);

__m128i s1 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow1 + x)), z);

__m128i s2 = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(srow2 + x)), z);

__m128i t0 = _mm_add_epi16(_mm_mullo_epi16(_mm_add_epi16(s0, s2), c3), _mm_mullo_epi16(s1, c10));

__m128i t1 = _mm_sub_epi16(s2, s0);

_mm_store_si128((__m128i*)(trow0 + x), t0);

_mm_store_si128((__m128i*)(trow1 + x), t1);

}

#endif

for( ; x < colsn; x++ )

{

int t0 = (srow0[x] + srow2[x])*3 + srow1[x]*10;

int t1 = srow2[x] - srow0[x];

trow0[x] = (deriv_type)t0;

trow1[x] = (deriv_type)t1;

}

// make border

int x0 = (cols > 1 ? 1 : 0)*cn, x1 = (cols > 1 ? cols-2 : 0)*cn;

for( int k = 0; k < cn; k++ )

{

trow0[-cn + k] = trow0[x0 + k]; trow0[colsn + k] = trow0[x1 + k];

trow1[-cn + k] = trow1[x0 + k]; trow1[colsn + k] = trow1[x1 + k];

}

// do horizontal convolution, interleave the results and store them to dst

x = 0;

#if CV_SSE2

for( ; x <= colsn - 8; x += 8 )

{

__m128i s0 = _mm_loadu_si128((const __m128i*)(trow0 + x - cn));

__m128i s1 = _mm_loadu_si128((const __m128i*)(trow0 + x + cn));

__m128i s2 = _mm_loadu_si128((const __m128i*)(trow1 + x - cn));

__m128i s3 = _mm_load_si128((const __m128i*)(trow1 + x));

__m128i s4 = _mm_loadu_si128((const __m128i*)(trow1 + x + cn));

__m128i t0 = _mm_sub_epi16(s1, s0);

__m128i t1 = _mm_add_epi16(_mm_mullo_epi16(_mm_add_epi16(s2, s4), c3), _mm_mullo_epi16(s3, c10));

__m128i t2 = _mm_unpacklo_epi16(t0, t1);

t0 = _mm_unpackhi_epi16(t0, t1);

// this can probably be replaced with aligned stores if we aligned dst properly.

_mm_storeu_si128((__m128i*)(drow + x*2), t2);

_mm_storeu_si128((__m128i*)(drow + x*2 + 8), t0);

}

#endif

for( ; x < colsn; x++ )

{

deriv_type t0 = (deriv_type)(trow0[x+cn] - trow0[x-cn]);

deriv_type t1 = (deriv_type)((trow1[x+cn] + trow1[x-cn])*3 + trow1[x]*10);

drow[x*2] = t0; drow[x*2+1] = t1;

}

}

}

標準LK算法的迭代

這個部分的實現需要注意的地方主要是subpixel的計算,因為每次計算出的位移都很小,考慮到計算的精度,必須的精確到小數位,所以需要注意如何計算一個小數位置的像素值,這個就和線性插值是類似的。如下圖所示,小數位置的像素值是有四個相鄰像素擬合出來的。設中間藍色像素點的坐標為(xsub,ysub),四周四個整數位置的像素點自分別為(x0,y0),(x0,y1),(x1,y0),(x1,y1),中間藍色像素離其他四個像素水平和垂直方向上的像素距離分別為w00,w01,w10,w11,如圖中所標。

61147728_3.pngsubpxiel的計算

subpxiel的計算公式為

I(xsub,ysub)=w11w01I(x0,y0)+w10w01I(x0,y1)+w11w00I(x1,y0)+w10w00I(x1,y1)

在計算權重wi,j的時候,為了避免浮點計算,會對wi,j乘以一定的倍數使用整數運算。用的subpixel的地方主要有兩個地方,一個是在計算矩陣G的時候,需要取梯度圖的值,但是點的位置不一定是整數,所以一定要使用subpixel取值;還有一個地方是計算b的時候,因為要取前后兩幅圖像的像素值,而這兩個點的位置也不一定是整數,所以也要用到subpixel。考慮到這部分的OpenCV代碼比較長,而理解相對沒有那么困難,這里就不再貼出,僅列出上面需要注意的地方,需要的可以參考oepncv2.4.8/video/lkpramid.cpp/line:159-483

尾聲

LK算法的實現除了以上所講的OpenCV的實現外,還有幾個其他的版本,分別是由Stan Birchfield實現的版本KLT,速率相比OpenCV慢一些;一個GPU加速實現的版本GPU KLT;一個Matlab實現的版本Matlab KLT以及一個Java實現的版本Java KLT。

Bruce D. Lucas and Takeo Kanade. An Iterative Image Registration Technique with an Application to Stereo Vision. International Joint Conference on Artificial Intelligence, pages 674-679, 1981.

Carlo Tomasi and Takeo Kanade. Detection and Tracking of Point Features. Carnegie Mellon University Technical Report CMU-CS-91-132, April 1991.

Jianbo Shi and Carlo Tomasi. Good Features to Track. IEEE Conference on Computer Vision and Pattern Recognition, pages 593-600, 1994.

Jean-Yves Bouguet. Pyramidal Implementation of the Lucas Kanade Feature Tracker.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/529505.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/529505.shtml
英文地址,請注明出處:http://en.pswp.cn/news/529505.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

php判斷url參數為空,PHP檢查url鏈接是否已經有參數的簡單示例

這篇文章主要為大家詳細介紹了PHP檢查url鏈接是否已經有參數的簡單示例&#xff0c;具有一定的參考價值&#xff0c;可以用來參考一下。感興趣的小伙伴&#xff0c;下面一起跟隨512筆記的小編小韻來看看吧&#xff01;比如分頁&#xff0c;因為有些鏈接已經有參數了&#xff0c…

java 讀取 郵件 附件,JavaMail 中對附件下載的處理

在項目中使用 JavaMail 作為郵件核心庫,但是一直遇到一個比較頭疼的問題,當郵件中存在附件時,此封郵件加載速度很慢,最高時之后 80k/s.通過 log 打印看出,當獲取附件時,獲取一段之后等待一段時間在獲取另一端.導致 io 開銷過大,等待網絡讀取時間過長.相關日志如下:O24 FETCH 27…

matlab求勒讓德多項式零點,有沒有勒讓德多項式導數 零點程序

求N1次勒讓德多項式的m(m0,1,...)階導數零點Matlab程序子程序&#xff1a;function xjp(N,alpha,beta)n1:N;a(1)(alphabeta2)/2;b(1)(beta-alpha)/2;a([2:N1])(2*nalphabeta1).*(2*nalphabeta2)./(2*(n1).*(nalphabeta1));b([2:N1])(alpha*alpha-beta*beta)*(2*nalphabeta1)./(…

js_long.php,protobuf.js 與 Long.js的使用詳解

這次給大家帶來protobuf.js 與 Long.js的使用詳解&#xff0c;是急用protobuf.js 與 Long.js的注意事項有哪些&#xff0c;下面就是實戰案例&#xff0c;一起來看一下。protobuf.js的結構和webpack的加載之后的結構很相似。這樣的模塊化組合是個不錯的結構方式。1個是適應了不同…

微分進化算法解決函數優化問題的matlab代碼,Matlab微分進化算法及優化函數測試...

微分進化(Difference Evolution&#xff0c;DE)算法是一種優化算法&#xff0c;據稱其比GA(遺傳算法)等更為優秀。借鑒網上實現的DE算法&#xff0c;用Matlab實現了對若干函數優化問題的解法&#xff0c;代碼如下&#xff1a;function [] de_testclear all;close all;clc;%解&…

Oracle中獲取文件中的數據,操作oracle中的數據文件

收藏于http://dev.yesky.com/296/8090796.shtmlOracle數據庫中管理表空間和數據文件數據庫的三大組成部分:數據文件&#xff0c;控制文件&#xff0c;Redo日志。表空間分為系統表空間和非系統表空間。SYSTEM表空間是最基本的&#xff0c;必須的&#xff0c;Oracle建議你為用戶數…

oracle 存儲過程設置回滾點,(轉)oracle 存儲過程事宜使用斷點回滾 -savepoint

學習存儲過程中使用斷點回滾事務時&#xff0c;發現目前網絡上存在一個問題&#xff0c;那就是使用斷點回滾后&#xff0c;都忘記了一個很重要的事情&#xff0c;提交事務。雖然使用了斷點回滾&#xff0c;但是斷點回滾不像rollBack或commit一樣結束當前事務&#xff0c;而使用…

php tp5清空數據表并主鍵,tp5數據庫——更新數據

更新數據更新數據表中的數據Db::table(think_user)->where(id, 1)->update([name > thinkphp]);如果數據中包含主鍵&#xff0c;可以直接使用&#xff1a;Db::table(think_user)->update([name > thinkphp,id>1]);update 方法返回影響數據的條數&#xff0c;…

oracle幾個網絡,ORACLE網絡的幾個重點概念

數據庫名(DB_NAME)、數據庫實例名(INSTANCE_NAME)、操作系統環境變量ORACLE_SID、數據庫服務名 (SERVICE_NAME)、數據庫域名(DB_DOMAIN)以及全局數據庫名(GLOBAL_DB_NAME)是幾個使用Oracle數據庫容易混 淆的概念。1、數據庫名與實例名數據庫名(DB_NAME)&#xff1a;是區分數據的…

oracle每季度補丁,Oracle 2020 年第四季度補丁發布

半個月前&#xff0c;也就是 10 月 20 日&#xff0c; Oracle 發布了今年最后一次補丁更新&#xff0c;那么很多人都想打最新的 PSU&#xff0c;理由是有被掃到各種漏洞&#xff0c;有的掃描工具著實太坑&#xff0c;這里就不用說了&#xff0c;前幾天看到蓋總發布的文章2020年…

php flash上傳進度條,PHP_PHP+FLASH實現上傳文件進度條相關文件 下載,PHP之所以很難實現上傳進度條 - phpStudy...

PHPFLASH實現上傳文件進度條相關文件 下載PHP之所以很難實現上傳進度條是因為在我們上傳文件到服務器的時候&#xff0c;要等到文件全部送到服務器之后&#xff0c;才執行相應的php文件。在這之前&#xff0c;文件數據保存在一個臨時文件里面&#xff0c;而php無法獲得這個文件…

oracle截取時間函數6,SQL 取日期部分函數

convert(varchar(10),getdate(),120)這個函數就可以得到日期部分了&#xff0c;我覺得還是比較方便的&#xff0c;我覺得SQL在日期方面&#xff0c;沒有ORACLE函數坐的好。。Sql Server中的日期與時間函數1. 當前系統日期、時間select getdate()2. dateadd 在向指定日期加上一…

oracle 找不到程序,Oracle 找不到監聽程序

Oracle服務器文件 app\Administrator\product\11.2.0\dbhome_1\NETWORK\ADMIN\listener.oraSID_LIST_LISTENER (SID_LIST (SID_DESC (SID_NAME CLRExtProc)(ORACLE_HOME D:\app\Administrator\product\11.2.0\dbhome_1)(PROGRAM extproc)(ENVS "EXTPROC_DLLSONLY:D:\…

oracle中的存儲過程教程,oracle 存儲過程

-- 查看系統定時任務SELECT * FROM DBA_JOBS-- 新建定時任務DECLAREjobno NUMBER;BEGINSYS.DBMS_JOB.SUBMIT(JOB > jobno, /*自動生成JOB_ID*/WHAT > qmcb_ls_data;, /*需要執行的存儲過程名稱或SQL語句*/NEXT_DATE > SYSDATE, /*初次執行時間-立即執行*/INTERVAL >…

Oracle segment啥意思,關于oracle數據庫段segment的小結

段(segment)是一種在數據庫中消耗物理存儲空間的任何實體(一個段可能存在于多個數據文件中&#xff0c;因為物理的數據文件是組成邏輯表空間的基本物理存儲單位)今天碰到一個高水位問題&#xff1a;一個分區表&#xff0c;刪除某個分區中的一些數據后&#xff0c;表空間并沒有回…

Linux在文件第一行添加字符,使用shell命令給文件中每一行的前面、后面、指定列添加字符...

shell給一個文件中的每一行開頭插入字符的方法&#xff1a;awk {print "X"$0} fileNameshell給一個文件中的每一行結尾插入字符的方法&#xff1a;awk {print $0"X"} fileNameshell給一個文件中的每一行的指定列插入字符的方法&#xff1a;awk $O$O" …

路由器 刷 linux系統版本,在Linux下用tftp刷寫路由器固件

(以Buffalo WHR-G300N V2路由器為例)以發行版Ubuntu為例(如果你在Windows下&#xff0c;可下載Ubuntu的ISO文件&#xff0c;再用wubi安裝Ubuntu&#xff0c;可免去重新分區的麻煩)&#xff0c;下載Buffalo WHR-G300N V2路由器的FTP版固件文件&#xff0c;放到Ubuntu的/home目錄…

linux沒有etho網卡,關于怎么解決CENTOS7沒有ETH0網卡這個問題

CentOS7系統安裝完畢之后&#xff0c;輸入ifconfig命令發現沒有eth0,不符合我們的習慣。而且也無法遠程ssh連接。1.進入目錄/etc/sysconfig/network-scripts/2.將文件ifcfg-ens33重命名為ifcfg-eth0;(注意&#xff1a;修改需要切換至root用戶)3.編輯ifcfg-eth0使用命令&#xf…

linux添加物理卷編輯文件夾,Red hat Linux下的邏輯卷管理器LVM-上

【IT168 專稿】Red hat 下的LVM 上 LVM是Logical Volume Manager(邏輯卷管理器)的簡寫&#xff0c;它為主機提供了更高層次的磁盤存儲管理能力。LVM可以幫助系統管理員為應用與用戶方便地分配存儲空間。在LVM管理下的邏輯卷可以按需改變大小或添加移除。另外&#xff0c;LVM可以…

linux的nfs端口號,#Linux NFS服務 固定端口及防火墻配置#

#Linux NFS服務 固定端口及防火墻配置#1.在Linux上正常安裝NFS服務2.修改/etc/service,添加以下內容(端口號必須在1024以下&#xff0c;且未被占用)# Local servicesmountd 1011/tcp #rpc.mountdmountd 1011/udp #rpc.mountdrquotad 1012/tcp #rpc.rquotadrquotad 1012/udp #rp…