學習OpenCV——Surf(特徵點篇)&flann
Surf(Speed Up Robust Feature)
Surf演算法的原理
1.構建Hessian矩陣構造高斯金字塔尺度空間
其實surf構造的金字塔影象與sift有很大不同,就是因為這些不同才加快了其檢測的速度。Sift採用的是DOG影象,而surf採用的是Hessian矩陣行列式近似值影象。Hessian矩陣是Surf演算法的核心,為了方便運算,假設函式f(z,y),Hessian矩陣H是由函式,偏導陣列成。首先來看看影象中某個畫素點的Hessian矩陣,如下:
即每一個畫素點都可以求出一個Hessian矩陣。
H矩陣判別式為:
判別式的值是H矩陣的特徵值,可以利用判定結果的符號將所有點分類,根據判別式取值正負,來判別該點是或不是極值點。
在SURF演算法中,用影象畫素l(x,y)即為函式值f(x,y),選用二階標準高斯函式作為濾波器,通過特定核間的卷積計算二階偏導數,這樣便能計算出H矩陣的三個矩陣元素L_xx,L_xy,L_yy
從而計算出H矩陣:
但是由於我們的特徵點需要具備尺度無關性,所以在進行Hessian矩陣構造前,需要對其進行高斯濾波。
這樣,經過濾波後在進行Hessian的計算,其公式如下:
L(x,t)是一幅影象在不同解析度下的表示,可以利用高斯核G(t)與影象函式 I(x) 在點x的卷積來實現,其中
g(x)為高斯函式,t為高斯方差。通過這種方法可以為影象中每個畫素計算出其H行列式的決定值,並用這個值來判別特徵點。為方便應用,Herbert
Bay提出用近似值現代替L(x,t)。為平衡準確值與近似值間的誤差引入權值叫,權值隨尺度變化,則H矩陣判別式可表示為:
其中0.9是作者給出的一個經驗值,其實它是有一套理論計算的,具體去看surf的英文論文。
由於求Hessian時要先高斯平滑,然後求二階導數,這在離散的畫素點是用模板卷積形成的,這2中操作合在一起用一個模板代替就可以了,比如說y方向上的模板如下:
該圖的左邊即用高斯平滑然後在y方向上求二階導數的模板,為了加快運算用了近似處理,其處理結果如右圖所示,這樣就簡化了很多。並且右圖可以採用積分圖來運算,大大的加快了速度,關於積分圖的介紹,可以去查閱相關的資料。
同理,x和y方向的二階混合偏導模板如下所示:
上面講的這麼多隻是得到了一張近似hessian行列式圖,這類似sift中的DOG圖,但是在金字塔影象中分為很多層,每一層叫做一個octave,每一個octave中又有幾張尺度不同的圖片。在sift演算法中,同一個octave層中的圖片尺寸(即大小)相同,但是尺度(即模糊程度)不同,而不同的octave層中的圖片尺寸大小也不相同,因為它是由上一層圖片降取樣得到的。在進行高斯模糊時,sift的高斯模板大小是始終不變的,只是在不同的octave之間改變圖片的大小。而在surf中,圖片的大小是一直不變的,不同的octave層得到的待檢測圖片是改變高斯模糊尺寸大小得到的,當然了,同一個octave中個的圖片用到的高斯模板尺度也不同。演算法允許尺度空間多層影象同時被處理,不需對影象進行二次抽樣,從而提高演算法效能。左圖是傳統方式建立一個如圖所示的金字塔結構,影象的寸是變化的,並且運
算會反覆使用高斯函式對子層進行平滑處理,右圖說明Surf演算法使原始影象保持不變而只改變濾波器大小。Surf採用這種方法節省了降取樣過程,其處理速度自然也就提上去了。其金字塔影象如下所示:
2. 利用非極大值抑制初步確定特徵點
此步驟和sift類似,將經過hessian矩陣處理過的每個畫素點與其3維領域的26個點進行大小比較,如果它是這26個點中的最大值或者最小值,則保留下來,當做初步的特徵點。檢測過程中使用與該尺度層影象解析度相對應大小的濾波器進行檢測,以3×3的濾波器為例,該尺度層影象中9個畫素點之一圖2檢測特徵點與自身尺度層中其餘8個點和在其之上及之下的兩個尺度層9個點進行比較,共26個點,圖2中標記‘x’的畫素點的特徵值若大於周圍畫素則可確定該點為該區域的特徵點。
3. 精確定位極值點
這裡也和sift演算法中的類似,採用3維線性插值法得到亞畫素級的特徵點,同時也去掉那些值小於一定閾值的點,增加極值使檢測到的特徵點數量減少,最終只有幾個特徵最強點會被檢測出來。
4. 選取特徵點的主方向。
這一步與sift也大有不同。Sift選取特徵點主方向是採用在特徵點領域內統計其梯度直方圖,取直方圖bin值最大的以及超過最大bin值80%的那些方向做為特徵點的主方向。
而在surf中,不統計其梯度直方圖,而是統計特徵點領域內的harr小波特徵。即在特徵點的領域(比如說,半徑為6s的圓內,s為該點所在的尺度)內,統計60度扇形內所有點的水平haar小波特徵和垂直haar小波特徵總和,haar小波的尺寸變長為4s,這樣一個扇形得到了一個值。然後60度扇形以一定間隔進行旋轉,最後將最大值那個扇形的方向作為該特徵點的主方向。該過程的示意圖如下:
5. 構造surf特徵點描述運算元
在sift中,是在特徵點周圍取16*16的鄰域,並把該領域化為4*4個的小區域,每個小區域統計8個方向梯度,最後得到4*4*8=128維的向量,該向量作為該點的sift描述子。
在surf中,也是在特徵點周圍取一個正方形框,框的邊長為20s(s是所檢測到該特徵點所在的尺度)。該框帶方向,方向當然就是第4步檢測出來的主方向了。然後把該框分為16個子區域,每個子區域統計25個畫素的水平方向和垂直方向的haar小波特徵,這裡的水平和垂直方向都是相對主方向而言的。該haar小波特徵為水平方向值之和,水平方向絕對值之和,垂直方向之和,垂直方向絕對值之和。該過程的示意圖如下所示:
這樣每個小區域就有4個值,所以每個特徵點就是16*4=64維的向量,相比sift而言,少了一半,這在特徵匹配過程中會大大加快匹配速度。
6.結束語
Surf採用Henssian矩陣獲取影象區域性最值還是十分穩定的,但是在求主方向階段太過於依賴區域性區域畫素的梯度方向,有可能使得找到的主方向不準確,後面的特徵向量提取以及匹配都嚴重依賴於主方向,即使不大偏差角度也可以造成後面特徵匹配的放大誤差,從而匹配不成功;另外影象金字塔的層取 得不足夠緊密也會使得尺度有誤差,後面的特徵向量提取同樣依賴相應的尺度,發明者在這個問題上的折中解決方法是取適量的層然後進行插值。 Sift是一種只 利用到灰度性質的演算法,忽略了色彩資訊,後面又出現了幾種據說比Surf更穩定的描述器其中一些利用到了色彩資訊,讓我們拭目以待吧。程式碼:
來源:OpenCV/sample/c中的find_obj.cpp程式碼
需仔細注意:
1.定位部分:通過透視變換,畫出了目標在影象中的位置,但是這麼做會浪費很多時間,可以改進:
2.flann尋找最近的臨近Keypoints:
//Constructs a nearest neighbor search index for a given dataset
//利用m_image構造 a set of randomized kd-trees 一系列隨機多維檢索樹;
cv::flann::Index flann_index(m_image, cv::flann::KDTreeIndexParams(4)); // using 4 randomized kdtrees
//利用Knn近鄰演算法檢索m_object;結果存入 m_indices, m_dists;
flann_index.knnSearch(m_object, m_indices, m_dists, 2, cv::flann::SearchParams(64) ); // maximum number of leafs checked
flann演算法有很多功能,
/*
* A Demo to OpenCV Implementation of SURF
* Further Information Refer to "SURF: Speed-Up Robust Feature"
* Author: Liu Liu
* [email protected]
*/
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/imgproc/imgproc_c.h"#include <iostream>
#include <vector>
#include <stdio.h>using namespace std;
void help()
{
printf(
"This program demonstrated the use of the SURF Detector and Descriptor using\n"
"either FLANN (fast approx nearst neighbor classification) or brute force matching\n"
"on planar objects.\n"
"Usage:\n"
"./find_obj <object_filename> <scene_filename>, default is box.png and box_in_scene.png\n\n");
return;
}// define whether to use approximate nearest-neighbor search
#define USE_FLANN
IplImage* image = 0;double compareSURFDescriptors( const float* d1, const float* d2, double best, int length )
{
double total_cost = 0;
assert( length % 4 == 0 );
for( int i = 0; i < length; i += 4 )
{
double t0 = d1[i ] - d2[i ];
double t1 = d1[i+1] - d2[i+1];
double t2 = d1[i+2] - d2[i+2];
double t3 = d1[i+3] - d2[i+3];
total_cost += t0*t0 + t1*t1 + t2*t2 + t3*t3;
if( total_cost > best )
break;
}
return total_cost;
}
int naiveNearestNeighbor( const float* vec, int laplacian,
const CvSeq* model_keypoints,
const CvSeq* model_descriptors )
{
int length = (int)(model_descriptors->elem_size/sizeof(float));
int i, neighbor = -1;
double d, dist1 = 1e6, dist2 = 1e6;
CvSeqReader reader, kreader;
cvStartReadSeq( model_keypoints, &kreader, 0 );
cvStartReadSeq( model_descriptors, &reader, 0 ); for( i = 0; i < model_descriptors->total; i++ )
{
const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
const float* mvec = (const float*)reader.ptr;
CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
if( laplacian != kp->laplacian )
continue;
d = compareSURFDescriptors( vec, mvec, dist2, length );
if( d < dist1 )
{
dist2 = dist1;
dist1 = d;
neighbor = i;
}
else if ( d < dist2 )
dist2 = d;
}
if ( dist1 < 0.6*dist2 )
return neighbor;
return -1;
}//用於找到兩幅影象之間匹配的點對,並把匹配的點對儲存在 ptpairs 向量中,其中物體(object)影象的特徵點
//及其相應的描述器(區域性特徵)分別儲存在 objectKeypoints 和 objectDescriptors,場景(image)影象的特
//徵點及其相應的描述器(區域性特徵)分別儲存在 imageKeypoints和 imageDescriptors
void findPairs( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors,
const CvSeq* imageKeypoints, const CvSeq* imageDescriptors, vector<int>& ptpairs )
{
int i;
CvSeqReader reader, kreader;
cvStartReadSeq( objectKeypoints, &kreader );
cvStartReadSeq( objectDescriptors, &reader );
ptpairs.clear(); for( i = 0; i < objectDescriptors->total; i++ )
{
const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
const float* descriptor = (const float*)reader.ptr;
CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
int nearest_neighbor = naiveNearestNeighbor( descriptor, kp->laplacian, imageKeypoints, imageDescriptors );
if( nearest_neighbor >= 0 )
{
ptpairs.push_back(i);
ptpairs.push_back(nearest_neighbor);
}
}
}//Fast Library for Approximate Nearest Neighbors(FLANN)
void flannFindPairs( const CvSeq*, const CvSeq* objectDescriptors,
const CvSeq*, const CvSeq* imageDescriptors, vector<int>& ptpairs )
{
int length = (int)(objectDescriptors->elem_size/sizeof(float)); cv::Mat m_object(objectDescriptors->total, length, CV_32F);
cv::Mat m_image(imageDescriptors->total, length, CV_32F);
// copy descriptors
CvSeqReader obj_reader;
float* obj_ptr = m_object.ptr<float>(0);
cvStartReadSeq( objectDescriptors, &obj_reader );
//objectDescriptors to m_object
for(int i = 0; i < objectDescriptors->total; i++ )
{
const float* descriptor = (const float*)obj_reader.ptr;
CV_NEXT_SEQ_ELEM( obj_reader.seq->elem_size, obj_reader );
memcpy(obj_ptr, descriptor, length*sizeof(float));
obj_ptr += length;
}
//imageDescriptors to m_image
CvSeqReader img_reader;
float* img_ptr = m_image.ptr<float>(0);
cvStartReadSeq( imageDescriptors, &img_reader );
for(int i = 0; i < imageDescriptors->total; i++ )
{
const float* descriptor = (const float*)img_reader.ptr;
CV_NEXT_SEQ_ELEM( img_reader.seq->elem_size, img_reader );
memcpy(img_ptr, descriptor, length*sizeof(float));
img_ptr += length;
} // find nearest neighbors using FLANN
cv::Mat m_indices(objectDescriptors->total, 2, CV_32S);
cv::Mat m_dists(objectDescriptors->total, 2, CV_32F);
//Constructs a nearest neighbor search index for a given dataset
//利用m_image構造 a set of randomized kd-trees 一系列隨機多維檢索樹;
cv::flann::Index flann_index(m_image, cv::flann::KDTreeIndexParams(4)); // using 4 randomized kdtrees
//利用Knn近鄰演算法檢索m_object;結果存入 m_indices, m_dists;
flann_index.knnSearch(m_object, m_indices, m_dists, 2, cv::flann::SearchParams(64) ); // maximum number of leafs checked int* indices_ptr = m_indices.ptr<int>(0);
float* dists_ptr = m_dists.ptr<float>(0);
for (int i=0;i<m_indices.rows;++i)
{
if (dists_ptr[2*i]<0.6*dists_ptr[2*i+1])
{
ptpairs.push_back(i);
ptpairs.push_back(indices_ptr[2*i]);
}
}
}//用於尋找物體(object)在場景(image)中的位置,位置資訊儲存在引數dst_corners中,引數src_corners由物
//體(object的width幾height等決定,其他部分引數如上findPairs
/* a rough implementation for object location */
int locatePlanarObject( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors,
const CvSeq* imageKeypoints, const CvSeq* imageDescriptors,
const CvPoint src_corners[4], CvPoint dst_corners[4] )
{
double h[9];
CvMat _h = cvMat(3, 3, CV_64F, h);
vector<int> ptpairs;
vector<CvPoint2D32f> pt1, pt2;
CvMat _pt1, _pt2;
int i, n;#ifdef USE_FLANN
flannFindPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#else
findPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#endif n = (int)(ptpairs.size()/2);
if( n < 4 )
return 0; pt1.resize(n);
pt2.resize(n);
for( i = 0; i < n; i++ )
{
pt1[i] = ((CvSURFPoint*)cvGetSeqElem(objectKeypoints,ptpairs[i*2]))->pt;
pt2[i] = ((CvSURFPoint*)cvGetSeqElem(imageKeypoints,ptpairs[i*2+1]))->pt;
} _pt1 = cvMat(1, n, CV_32FC2, &pt1[0] );
_pt2 = cvMat(1, n, CV_32FC2, &pt2[0] );
if( !cvFindHomography( &_pt1, &_pt2, &_h, CV_RANSAC, 5 ))//計算兩個平面之間的透視變換
return 0; for( i = 0; i < 4; i++ )
{
double x = src_corners[i].x, y = src_corners[i].y;
double Z = 1./(h[6]*x + h[7]*y + h[8]);
double X = (h[0]*x + h[1]*y + h[2])*Z;
double Y = (h[3]*x + h[4]*y + h[5])*Z;
dst_corners[i] = cvPoint(cvRound(X), cvRound(Y));
} return 1;
}
int main(int argc, char** argv)
{
//物體(object)和場景(scene)的影象向來源
const char* object_filename = argc == 3 ? argv[1] : "D:/src.jpg";
const char* scene_filename = argc == 3 ? argv[2] : "D:/Demo.jpg"; help(); IplImage* object = cvLoadImage( object_filename, CV_LOAD_IMAGE_GRAYSCALE );
IplImage* image = cvLoadImage( scene_filename, CV_LOAD_IMAGE_GRAYSCALE );
if( !object || !image )
{
fprintf( stderr, "Can not load %s and/or %s\n",
object_filename, scene_filename );
exit(-1);
}
//記憶體儲存器
CvMemStorage* storage = cvCreateMemStorage(0); cvNamedWindow("Object", 1);
cvNamedWindow("Object Correspond", 1); static CvScalar colors[] =
{
{{0,0,255}},
{{0,128,255}},
{{0,255,255}},
{{0,255,0}},
{{255,128,0}},
{{255,255,0}},
{{255,0,0}},
{{255,0,255}},
{{255,255,255}}
};
IplImage* object_color = cvCreateImage(cvGetSize(object), 8, 3);
cvCvtColor( object, object_color, CV_GRAY2BGR ); CvSeq* objectKeypoints = 0, *objectDescriptors = 0;
CvSeq* imageKeypoints = 0, *imageDescriptors = 0;
int i;
/*
CvSURFParams params = cvSURFParams(500, 1);//SURF引數設定:閾值500,生成128維描述符
cvSURFParams 函式原型如下:
CvSURFParams cvSURFParams(double threshold, int extended)
{
CvSURFParams params;
params.hessianThreshold = threshold; // 特徵點選取的 hessian 閾值
params.extended = extended; // 是否擴充套件,1 - 生成128維描述符,0 - 64維描述符
params.nOctaves = 4;
params.nOctaveLayers = 2;
return params;
}
*/
CvSURFParams params = cvSURFParams(500, 1); double tt = (double)cvGetTickCount();//計時
/*
提取影象中的特徵點,函式原型:
CVAPI(void) cvExtractSURF( const CvArr* img, const CvArr* mask,
CvSeq** keypoints, CvSeq** descriptors,
CvMemStorage* storage, CvSURFParams params, int useProvidedKeyPts CV_DEFAULT(0) );
第3、4個引數返回結果:特徵點和特徵點描述符,資料型別是指標的指標,
*/
cvExtractSURF( object, 0, &objectKeypoints, &objectDescriptors, storage, params );
printf("Object Descriptors: %d\n", objectDescriptors->total); cvExtractSURF( image, 0, &imageKeypoints, &imageDescriptors, storage, params );
printf("Image Descriptors: %d\n", imageDescriptors->total);
tt = (double)cvGetTickCount() - tt; printf( "Extraction time = %gms\n", tt/(cvGetTickFrequency()*1000.));
CvPoint src_corners[4] = {{0,0}, {object->width,0}, {object->width, object->height}, {0, object->height}};
//定義感興趣的區域
CvPoint dst_corners[4];
IplImage* correspond = cvCreateImage( cvSize(image->width, object->height+image->height), 8, 1 );
//設定感興趣區域
//形成一大一小兩幅圖顯示在同一視窗
cvSetImageROI( correspond, cvRect( 0, 0, object->width, object->height ) );
cvCopy( object, correspond );
cvSetImageROI( correspond, cvRect( 0, object->height, correspond->width, correspond->height ) );
cvCopy( image, correspond );
cvResetImageROI( correspond );#ifdef USE_FLANN
printf("Using approximate nearest neighbor search\n");
#endif
//尋找物體(object)在場景(image)中的位置,並將資訊儲存(矩形框)
if( locatePlanarObject( objectKeypoints, objectDescriptors, imageKeypoints,
imageDescriptors, src_corners, dst_corners ))
{
for( i = 0; i < 4; i++ )
{
CvPoint r1 = dst_corners[i%4];
CvPoint r2 = dst_corners[(i+1)%4];
cvLine( correspond, cvPoint(r1.x, r1.y+object->height ),
cvPoint(r2.x, r2.y+object->height ), colors[8] );
}
}
//定義並儲存物體(object)在場景(image)圖形之間的匹配點對,並將其儲存在向量 ptpairs 中,之後可以對
//ptpairs 進行操作
vector<int> ptpairs;
#ifdef USE_FLANN
flannFindPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#else
findPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#endif
//顯示匹配結果(直線連線)
for( i = 0; i < (int)ptpairs.size(); i += 2 )
{
CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, ptpairs[i] );
CvSURFPoint* r2 = (CvSURFPoint*)cvGetSeqElem( imageKeypoints, ptpairs[i+1] );
cvLine( correspond, cvPointFrom32f(r1->pt),
cvPoint(cvRound(r2->pt.x), cvRound(r2->pt.y+object->height)), colors[8] );
} cvShowImage( "Object Correspond", correspond );
//顯示物體(object)的所有特徵點
for( i = 0; i < objectKeypoints->total; i++ )
{
CvSURFPoint* r = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, i );
CvPoint center;
int radius;
center.x = cvRound(r->pt.x);
center.y = cvRound(r->pt.y);
radius = cvRound(r->size*1.2/9.*2);
cvCircle( object_color, center, radius, colors[0], 1, 8, 0 );
}
cvShowImage( "Object", object_color ); cvWaitKey(0); //釋放視窗所佔用的記憶體
cvDestroyWindow("Object");
cvDestroyWindow("Object Correspond"); return 0;
}
相關推薦
學習OpenCV——Surf(特徵點篇)&flann快速最近鄰搜尋演算法
Surf(Speed Up Robust Feature) Surf演算法的原理 1.構建Hessian矩陣構造
學習OpenCV——Surf(特徵點篇)&flann
Surf(Speed Up Robust Feature) Surf演算法的原理 1.構建Hessian矩陣構造高斯金字塔
OpenCV特徵點檢測------Surf(特徵點篇)
Surf(Speed Up Robust Feature)Surf演算法的原理 1.構建Hessian矩陣構造高斯金字塔尺度空間其
學習OpenCV——HoG特徵詳解(特徵點篇)
HOG即histogram of oriented gradient, 是用於目標檢測的特徵描述子,該技術將影象區域性出現的方向梯度次數進行計數,該方法和邊緣方向直方圖、scale-invariant feature transform類似,不同的是hog的計算基於一致空
Dlib機器學習庫學習系列三----人臉對齊(特徵點檢測)
本篇部落格是Dlib庫學習的第三篇---人臉對齊。人臉對齊與人臉檢測工程建立與配置基本相同,在此不再贅述。可參照我上一篇部落格。閒話少說,來點乾貨。 步驟一:建立並配置工程,參照上一篇部落格。 步驟二:下載形狀模型檔案 下載地址
機器學習面試總結(第三篇)
9、整合學習大致分類?通俗理解怎樣才能提高整合學習的效能? 10、Booststrap sampling需要解決的問題?Booststrap sampling的思想?Bagging的基本思想?從偏差方差角度解釋bagging? 11、隨機森林RandomForest的思想?RF與bagg
學習OpenCV範例(二十一)——Keypoints+Knn+findHomography進行目標定位
本範例的程式碼主要都是 學習OpenCV——通過KeyPoints進行目標定位這篇部落格提供的,然後在它的基礎上稍加修改,檢測keypoints點的檢測器是SURF,獲取描述子也是用到SURF來描述,而用到的匹配器是FlannBased,匹配的方式是Knn方式,最後通過f
ftk學習記(輸入框篇)
file creat 之前 egl init -s 自己 效果 ast 【 聲明:版權全部。歡迎轉載。請勿用於商業用途。 聯系信箱:feixiaoxing @163.com】 昨天講了進度條,我們還是看看它的運行效果是怎麽樣的。截圖例如以下, 進度條使用的
Hadoop學習筆記—15.HBase框架學習(基礎知識篇)
dfs hdfs keep 負載均衡 包含 兩個 列族 文件存儲 version HBase是Apache Hadoop的數據庫,能夠對大型數據提供隨機、實時的讀寫訪問。HBase的目標是存儲並處理大型的數據。HBase是一個開源的,分布式的,多版本的,面向列的存儲模型,它
關於網絡運維學習的階段性簡要總結(華為篇)
華為 學習總結 眾所周知,學習是一件反人類的事情,但是你要從中發現它的有趣之處,便可妙趣橫生。學會是一件重要的事,會總結很重要。今天就總結一下我的學習。 A代表同一網段 B代表不同網段 GW代表網關 IP------A------IP
機器學習的數學基礎(線性代數篇)
ima 技術 分享 機器學習 線性代數 http src .com jpg 註:總結來自黃海廣博士。 機器學習的數學基礎(線性代數篇)
DLIB庫example中3d_point_cloud_ex.cpp(3D點雲)學習與實現
!免責!以下全是個人理解,新人菜鳥難免出錯,希望指正。 一、函式學習 1、perspective_window::overlay_dot (類,繼承自drawable_window, noncopyable)perspective_window: This is a simple win
設計模式大雜燴(24種設計模式的總結以及學習設計模式的幾點建議)
作者:zuoxiaolong8810(左瀟龍),轉載請註明出處,特別說明:本博文來自博主原部落格,為保證新部落格中博文的完整性,特複製到此留存,如需轉載請註明新部落格地址即可。 迄今為止
Unity學習(三)Unity Shader入門(基礎知識篇)+線性代數複習(未完待續)
至於為什麼剛建立了指令碼,現在就要做Shader了。。說多了都是淚 1.建立一個新的材質 Material Assert -> Create -> Material 拖到Scene中的某個物體上 2.建立一個新的Shader Assert -> Create -
誰說菜鳥不會資料分析(入門篇)----- 學習筆記6(資料分析報告)
1、資料分析報告:三大作用四項基本原則 定義 是根據資料分析原理和方法,運用資料來反映、研究和分析某項事物的現狀、問題、原因、本質和規律,並得出結論,提出解決辦法的一種分析應用文體。 這種文體是決策者認識事物、瞭解事物、
誰說菜鳥不會資料分析(入門篇)----- 學習筆記4(資料分析方法)
1、資料分析方法 資料分析作用與對應的分析方法 資料分析作用 基本方法 資料分析方法 現狀分析 對比 對比分析、平均分析、總和評價分析 原因分析
【itext學習之路】-------(第五篇)對pdf進行蓋章/簽章/數字簽名
在上一篇文章中,我們學習了使用itext對pdf增加圖片水印和文字水印,那麼這篇文章我們將要學習更高階一點的水印—-印章。可能你會有疑問,印章不也是一個圖片嗎?當然,你可以把一個印章圖片來做成圖片水印,但是我們這裡要介紹的是,通過數字簽名的方式來進行pdf簽章。
【itext學習之路】-------(第四篇)給pdf增加文字水印和圖片水印
一般而言,許多公司在做pdf之後,都會將公司的logo或者網址以水印的方式新增到pdf檔案中。本篇文章,我們將介紹在製作pdf的時候,如何將水印加入到pdf中去。 package cn.tomtocc.pdf; import java.io.FileNotFoundE
【itext學習之路】-------(第三篇)對pdf文件進行加密和許可權設定
上篇文章,我們學習了pdf的屬性設定,但是我們知道,在實際開發中,如果pdf文件被黑客盜取的話,那麼pdf中的資訊就會被洩露,因此本篇文章將會介紹pdf的加密設定,並且設定許可權。 首先我們要說明的是,itext中對pdf文件的加密包括兩部分,第一部分是使用者密
kafka學習一(基本命令篇)
1、前臺程序啟動: ./kafka-server-start.sh ../config/server.properties & 2、後臺程序啟動: ./kafka-server-start.sh -daemon ../config/server.properti