当前位置:网站首页>Code and principle of RANSAC
Code and principle of RANSAC
2022-06-27 19:17:00 【Wang bupian】
One . RANSAC Code principle
:https://blog.csdn.net/robinhjwy/article/details/79174914
Two . RANSAC Of Opencv Code
Only excerpts and RANSAC Relevant part , Extract from :https://blog.csdn.net/Darlingqiang/article/details/79775542
- OpenCV This function is implemented by calling findHomography Function call , Here is a routine :
[cpp] view plain copy
#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat obj=imread("F:\\Picture\\obj.jpg"); // Load target image
Mat scene=imread("F:\\Picture\\scene.jpg"); // Load scene image
if (obj.empty() || scene.empty() )
{
cout<<"Can't open the picture!\n";
return 0;
}
vector<KeyPoint> obj_keypoints,scene_keypoints;
Mat obj_descriptors,scene_descriptors;
ORB detector; // use ORB The algorithm extracts feature points
detector.detect(obj,obj_keypoints);
detector.detect(scene,scene_keypoints);
detector.compute(obj,obj_keypoints,obj_descriptors);
detector.compute(scene,scene_keypoints,scene_descriptors);
BFMatcher matcher(NORM_HAMMING,true); // Hamming distance as a measure of similarity
vector<DMatch> matches;
matcher.match(obj_descriptors, scene_descriptors, matches);
Mat match_img;
drawMatches(obj,obj_keypoints,scene,scene_keypoints,matches,match_img);
imshow(" Before filtering out mismatches ",match_img);
// Save matching sequence number
vector<int> queryIdxs( matches.size() ), trainIdxs( matches.size() );
for( size_t i = 0; i < matches.size(); i++ )
{
queryIdxs[i] = matches[i].queryIdx;
trainIdxs[i] = matches[i].trainIdx;
}
Mat H12; // Transformation matrix
vector<Point2f> points1; KeyPoint::convert(obj_keypoints, points1, queryIdxs);
vector<Point2f> points2; KeyPoint::convert(scene_keypoints, points2, trainIdxs);
int ransacReprojThreshold = 5; // Reject threshold
H12 = findHomography( Mat(points1), Mat(points2), CV_RANSAC, ransacReprojThreshold );
vector<char> matchesMask( matches.size(), 0 );
Mat points1t;
perspectiveTransform(Mat(points1), points1t, H12);
for( size_t i1 = 0; i1 < points1.size(); i1++ ) // preservation ‘ Inside point ’
{
if( norm(points2[i1] - points1t.at<Point2f>((int)i1,0)) <= ransacReprojThreshold ) // Mark the inner point
{
matchesMask[i1] = 1;
}
}
Mat match_img2; // Filter out ‘ External point ’ after
drawMatches(obj,obj_keypoints,scene,scene_keypoints,matches,match_img2,Scalar(0,0,255),Scalar::all(-1),matchesMask);
// Draw the target position
std::vector<Point2f> obj_corners(4);
obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( obj.cols, 0 );
obj_corners[2] = cvPoint( obj.cols, obj.rows ); obj_corners[3] = cvPoint( 0, obj.rows );
std::vector<Point2f> scene_corners(4);
perspectiveTransform( obj_corners, scene_corners, H12);
line( match_img2, scene_corners[0] + Point2f(static_cast<float>(obj.cols), 0),
scene_corners[1] + Point2f(static_cast<float>(obj.cols), 0),Scalar(0,0,255),2);
line( match_img2, scene_corners[1] + Point2f(static_cast<float>(obj.cols), 0),
scene_corners[2] + Point2f(static_cast<float>(obj.cols), 0),Scalar(0,0,255),2);
line( match_img2, scene_corners[2] + Point2f(static_cast<float>(obj.cols), 0),
scene_corners[3] + Point2f(static_cast<float>(obj.cols), 0),Scalar(0,0,255),2);
line( match_img2, scene_corners[3] + Point2f(static_cast<float>(obj.cols), 0),
scene_corners[0] + Point2f(static_cast<float>(obj.cols), 0),Scalar(0,0,255),2);
imshow(" After filtering out mismatches ",match_img2);
waitKey(0);
return 0;
}
- RANSAC The source code parsing
(1)findHomography Internal calls cvFindHomography function
srcPoints: Target image point set
dstPoints: Scene image point set
method: Minimum median method 、RANSAC Method 、 Least square method
ransacReprojTheshold: Projection error threshold
mask: Mask
[cpp] view plain copy
cvFindHomography( const CvMat* objectPoints, const CvMat* imagePoints,
CvMat* __H, int method, double ransacReprojThreshold,
CvMat* mask )
{
const double confidence = 0.995; // Degree of confidence
const int maxIters = 2000; // The maximum number of iterations
const double defaultRANSACReprojThreshold = 3; // Default reject threshold
bool result = false;
Ptr<CvMat> m, M, tempMask;
double H[9];
CvMat matH = cvMat( 3, 3, CV_64FC1, H ); // Transformation matrix
int count;
CV_Assert( CV_IS_MAT(imagePoints) && CV_IS_MAT(objectPoints) );
count = MAX(imagePoints->cols, imagePoints->rows);
CV_Assert( count >= 4 ); // There are at least 4 Samples
if( ransacReprojThreshold <= 0 )
ransacReprojThreshold = defaultRANSACReprojThreshold;
m = cvCreateMat( 1, count, CV_64FC2 ); // Convert to homogeneous coordinates
cvConvertPointsHomogeneous( imagePoints, m );
M = cvCreateMat( 1, count, CV_64FC2 );// Convert to homogeneous coordinates
cvConvertPointsHomogeneous( objectPoints, M );
if( mask )
{
CV_Assert( CV_IS_MASK_ARR(mask) && CV_IS_MAT_CONT(mask->type) &&
(mask->rows == 1 || mask->cols == 1) &&
mask->rows*mask->cols == count );
}
if( mask || count > 4 )
tempMask = cvCreateMat( 1, count, CV_8U );
if( !tempMask.empty() )
cvSet( tempMask, cvScalarAll(1.) );
CvHomographyEstimator estimator(4);
if( count == 4 )
method = 0;
if( method == CV_LMEDS ) // Minimum median method
result = estimator.runLMeDS( M, m, &matH, tempMask, confidence, maxIters );
else if( method == CV_RANSAC ) // use RANSAC Algorithm
result = estimator.runRANSAC( M, m, &matH, tempMask, ransacReprojThreshold, confidence, maxIters);//(2) analysis
else
result = estimator.runKernel( M, m, &matH ) > 0; // Least square method
if( result && count > 4 )
{
icvCompressPoints( (CvPoint2D64f*)M->data.ptr, tempMask->data.ptr, 1, count ); // Save the inner point set
count = icvCompressPoints( (CvPoint2D64f*)m->data.ptr, tempMask->data.ptr, 1, count );
M->cols = m->cols = count;
if( method == CV_RANSAC ) //
estimator.runKernel( M, m, &matH ); // The transformation matrix is re estimated by the least square method on the inner point set
estimator.refine( M, m, &matH, 10 );//
}
if( result )
cvConvert( &matH, __H ); // Save the transformation matrix
if( mask && tempMask )
{
if( CV_ARE_SIZES_EQ(mask, tempMask) )
cvCopy( tempMask, mask );
else
cvTranspose( tempMask, mask );
}
return (int)result;
}
(2) runRANSAC
maxIters: Maximum number of iterations
m1,m2 : Data samples
model: Transformation matrix
reprojThreshold: Projection error threshold
confidence: Degree of confidence 0.995
[cpp] view plain copy
bool CvModelEstimator2::runRANSAC( const CvMat* m1, const CvMat* m2, CvMat* model,
CvMat* mask0, double reprojThreshold,
double confidence, int maxIters )
{
bool result = false;
cv::Ptr<CvMat> mask = cvCloneMat(mask0);
cv::Ptr<CvMat> models, err, tmask;
cv::Ptr<CvMat> ms1, ms2;
int iter, niters = maxIters;
int count = m1->rows*m1->cols, maxGoodCount = 0;
CV_Assert( CV_ARE_SIZES_EQ(m1, m2) && CV_ARE_SIZES_EQ(m1, mask) );
if( count < modelPoints )
return false;
models = cvCreateMat( modelSize.height*maxBasicSolutions, modelSize.width, CV_64FC1 );
err = cvCreateMat( 1, count, CV_32FC1 );// Save the projection error corresponding to each group of points
tmask = cvCreateMat( 1, count, CV_8UC1 );
if( count > modelPoints ) // More than 4 A little bit
{
ms1 = cvCreateMat( 1, modelPoints, m1->type );
ms2 = cvCreateMat( 1, modelPoints, m2->type );
}
else // be equal to 4 A little bit
{
niters = 1; // One iteration
ms1 = cvCloneMat(m1); // Save every randomly found sample point
ms2 = cvCloneMat(m2);
}
for( iter = 0; iter < niters; iter++ ) // Continuous iteration
{
int i, goodCount, nmodels;
if( count > modelPoints )
{
// stay (3) analysis getSubset
bool found = getSubset( m1, m2, ms1, ms2, 300 ); // Random selection 4 Group point , And the three points are not collinear ,(3) analysis
if( !found )
{
if( iter == 0 )
return false;
break;
}
}
nmodels = runKernel( ms1, ms2, models ); // Estimate the approximate transformation matrix , return 1
if( nmodels <= 0 )
continue;
for( i = 0; i < nmodels; i++ )// Do it once
{
CvMat model_i;
cvGetRows( models, &model_i, i*modelSize.height, (i+1)*modelSize.height );// obtain 3×3 Matrix elements
goodCount = findInliers( m1, m2, &model_i, err, tmask, reprojThreshold ); // Find the inner point ,(4) analysis
if( goodCount > MAX(maxGoodCount, modelPoints-1) ) // The number of elements in the current interior point set is greater than the number of elements in the optimal interior point set
{
std::swap(tmask, mask);
cvCopy( &model_i, model ); // Update the optimal model
maxGoodCount = goodCount;
//confidence Default for confidence 0.995,modelPoints Is the minimum number of samples required =4,niters The number of iterations
niters = cvRANSACUpdateNumIters( confidence, // Update iterations ,(5) analysis
(double)(count - goodCount)/count, modelPoints, niters );
}
}
}
if( maxGoodCount > 0 )
{
if( mask != mask0 )
cvCopy( mask, mask0 );
result = true;
}
return result;
}
step one niters The initial value is 2000, This is the initial RANSAC The number of cycles of the algorithm ,getSubset() Functions are randomly selected from a set of corresponding sequences 4 Group ( Because if you want to calculate a 3X3 Matrix , Need at least 4 Coordinates corresponding to the group ),m1 and m2 We input the sequence ,ms1 and ms2 Is a random selection of the corresponding 4 Group matching .
Choose at random 4 After group matching , It should be based on this 4 A match calculates the corresponding matrix , So the function runKernel() It is based on 4 Group matching calculation matrix , In the parameter models Is the matrix .
stept wo This matrix is just an assumption , To test this hypothesis , Need to use other points to calculate , See if the other points are inner points or outer points .
findInliers() Function is used to calculate the interior point . Using the matrix obtained above , Bring all the sequences into , Calculate which are interior points , What are the outliers , The return value of the function is goodCount, Is the number of interior points calculated this time .
step three Another value in the function is maxGoodCount, The maximum number of interior points per cycle is stored in this value , If an estimated matrix has more interior points , The more likely this matrix is to be correct .
step four So after calculating the number of interior points , Then judge goodCount and maxGoodCount The size of the relationship , If goodCount>maxGoodCount, Then put goodCount Assign a value to maxGoodCount. The one line of code after the assignment is critical , Let's talk about it alone , The code is as follows :
niters = cvRANSACUpdateNumIters( confidence,
(double)(count - goodCount)/count, modelPoints, niters );
niters It was originally the number of iterations , That is, the number of cycles . But through this line of code, we find that , After each cycle , Will be right. niters This value is updated , That is, the total number of cycles will be changed after each cycle .
cvRANSACUpdateNumIters() Functions use confidence( Degree of confidence )count( Total number of matches )goodCount( Current number of interior points )niters( Current total iterations ) These parameters , To dynamically change the total number of iterations . The central idea of this function is that when the interior points account for a large proportion , Then it's likely that the correct estimate has been found , So reduce the number of iterations to save time . The number of iterations is reduced exponentially , So the time cost saved is also very considerable . So the original design 2000 Iterations of , Maybe the final number of iterations is only a few tens . alike , If you set the number of iterations to 10000 Or bigger , After several iterations ,niters It will become very small again . So at the beginning niters No matter how big it is , In fact, it has no effect on the final running time .
therefore , You should now know why the input data has increased , The running time of the algorithm will not increase .openCV Of RANSAC The algorithm first sets the number of iterations to 2000, And then in the process of iteration , Change the total number of iterations dynamically , No matter how much data is entered , The total number of iterations does not increase , And through 4 The time of the matrix estimated by matching calculation is constant , The interior point is calculated by estimating the matrix , The increased time cost in this area can be basically ignored . So the end result is , No matter how many input points , The operation time will not change much .
That's all RANSAC The core code of the algorithm , Some of the functions used , The following are given one by one .
(3)getSubset
ms1,ms2: Save randomly found 4 Samples
maxAttempts: Maximum number of iterations ,300
[cpp] view plain copy
bool CvModelEstimator2::getSubset( const CvMat* m1, const CvMat* m2,
CvMat* ms1, CvMat* ms2, int maxAttempts )
{
cv::AutoBuffer<int> _idx(modelPoints); //modelPoints The minimum number of sample points required
int* idx = _idx;
int i = 0, j, k, idx_i, iters = 0;
int type = CV_MAT_TYPE(m1->type), elemSize = CV_ELEM_SIZE(type);
const int *m1ptr = m1->data.i, *m2ptr = m2->data.i;
int *ms1ptr = ms1->data.i, *ms2ptr = ms2->data.i;
int count = m1->cols*m1->rows;
assert( CV_IS_MAT_CONT(m1->type & m2->type) && (elemSize % sizeof(int) == 0) );
elemSize /= sizeof(int); // The number of bytes occupied by each data
for(; iters < maxAttempts; iters++)
{
for( i = 0; i < modelPoints && iters < maxAttempts; )
{
idx[i] = idx_i = cvRandInt(&rng) % count; // Random selection 1 Group point
for( j = 0; j < i; j++ ) // Check whether the selection is repeated
if( idx_i == idx[j] )
break;
if( j < i )
continue; // To choose
for( k = 0; k < elemSize; k++ ) // Copy point data
{
ms1ptr[i*elemSize + k] = m1ptr[idx_i*elemSize + k];
ms2ptr[i*elemSize + k] = m2ptr[idx_i*elemSize + k];
}
if( checkPartialSubsets && (!checkSubset( ms1, i+1 ) || !checkSubset( ms2, i+1 )))// Check whether the points are collinear
{
iters++; // If collinear , Re select a group
continue;
}
i++;
}
if( !checkPartialSubsets && i == modelPoints &&
(!checkSubset( ms1, i ) || !checkSubset( ms2, i )))
continue;
break;
}
return i == modelPoints && iters < maxAttempts; // Returns the number of sample points found
}
(4) findInliers & computerReprojError
[cpp] view plain copy
int CvModelEstimator2::findInliers( const CvMat* m1, const CvMat* m2,
const CvMat* model, CvMat* _err,
CvMat* _mask, double threshold )
{
int i, count = _err->rows*_err->cols, goodCount = 0;
const float* err = _err->data.fl;
uchar* mask = _mask->data.ptr;
computeReprojError( m1, m2, model, _err ); // Calculate the projection error of each group of points
threshold *= threshold;
for( i = 0; i < count; i++ )
goodCount += mask[i] = err[i] <= threshold;// The error is within the limit , Join in ‘ Interior point set ’
return goodCount;
}
//--------------------------------------
void CvHomographyEstimator::computeReprojError( const CvMat* m1, const CvMat* m2,const CvMat* model, CvMat* _err )
{
int i, count = m1->rows*m1->cols;
const CvPoint2D64f* M = (const CvPoint2D64f*)m1->data.ptr;
const CvPoint2D64f* m = (const CvPoint2D64f*)m2->data.ptr;
const double* H = model->data.db;
float* err = _err->data.fl;
for( i = 0; i < count; i++ ) // Save the projection error of each group of points , Corresponding to the above transformation formula
{
double ww = 1./(H[6]*M[i].x + H[7]*M[i].y + 1.);
double dx = (H[0]*M[i].x + H[1]*M[i].y + H[2])*ww - m[i].x;
double dy = (H[3]*M[i].x + H[4]*M[i].y + H[5])*ww - m[i].y;
err[i] = (float)(dx*dx + dy*dy);
}
}
(5)cvRANSACUpdateNumIters
Corresponding to the above k Calculation formula
p: Degree of confidence
ep: Outer point ratio
[cpp] view plain copy
cvRANSACUpdateNumIters( double p, double ep,
int model_points, int max_iters )
{
if( model_points <= 0 )
CV_Error( CV_StsOutOfRange, "the number of model points should be positive" );
p = MAX(p, 0.);
p = MIN(p, 1.);
ep = MAX(ep, 0.);
ep = MIN(ep, 1.);
// avoid inf's & nan's
double num = MAX(1. - p, DBL_MIN); //num=1-p, Make molecules
double denom = 1. - pow(1. - ep,model_points);// Be a denominator
if( denom < DBL_MIN )
return 0;
num = log(num);
denom = log(denom);
return denom >= 0 || -num >= max_iters*(-denom) ?
max_iters : cvRound(num/denom);
}
Reference resources https://blog.csdn.net/laobai1015/article/details/51683076
cv::findFundamentalMat
The knowledge of epipolar geometry is often used in processing stereo image pairs , It is also very common to calculate the basic matrix .OpenCV The algorithm of basic matrix is realized . For the old version of C Code , Calculate the fundamental matrix RANSAC There is a method in which the number of iterations is not convergent bug, It may cause the number of samples per calculation to reach the maximum limit , The root cause is that the formula for calculating the sampling reliability is wrong , The new version of the code has not been looked at carefully , I'm not sure if I've fixed this bug. But this bug It doesn't make much difference to the final result , It will only affect the efficiency of calculation —— Originally, a few samples can end the iteration , In this bug It may take hundreds of samples under the influence of .
There are also some problems in the use of the new version of the function to calculate the basic matrix , So let's see cv::findFundamentalMat function :
//! the algorithm for finding fundamental matrix
enum
{
FM_7POINT = CV_FM_7POINT, //!< 7-point algorithm
FM_8POINT = CV_FM_8POINT, //!< 8-point algorithm
FM_LMEDS = CV_FM_LMEDS, //!< least-median algorithm
FM_RANSAC = CV_FM_RANSAC //!< RANSAC algorithm
};
//! finds fundamental matrix from a set of corresponding 2D points
CV_EXPORTS Mat findFundamentalMat( const Mat& points1, const Mat& points2,
CV_OUT vector<uchar>& mask, int method=FM_RANSAC,
double param1=3., double param2=0.99 );
//! finds fundamental matrix from a set of corresponding 2D points
CV_EXPORTS_W Mat findFundamentalMat( const Mat& points1, const Mat& points2,
int method=FM_RANSAC,
double param1=3., double param2=0.99 );
It's on it OpenCV Calculate the fundamental matrix C++ Interface , Its internal implementation is still invoked C Code function , It is only a single encapsulation on the upper layer . Some documents on the Internet say const Mat& points1 and points2 These two parameters can be directly determined by vector Type as an incoming parameter , In fact, this is wrong . Direct use vector Pass on , There may be no error during compilation , But the runtime will crash directly . because cv::Mat The constructor of does not put Point2f Converted to two floating-point numbers and stored in Mat Of the two elements of , But still Point2f Stored in Mat In an element of , therefore findFundamentalMat As soon as I read this Mat Will go wrong .
So we'd better build it honestly Mat points1 and Mat points2. The dimension of the matrix can be 2xN, It can also be Nx2 Of , among N Is the number of characteristic points . Another thing to note is that the type of the matrix cannot be CV_64F, in other words findFundamentalMat Internal analysis points1 and points2 According to float Type to resolve , Set to CV_64F Will cause data reading failure , Program crash . It is better to set it to CV_32F. The following is calculated from the matched feature points F Code example of :
// vector<KeyPoint> m_LeftKey;
// vector<KeyPoint> m_RightKey;
// vector<DMatch> m_Matches;
// The above three variables have been calculated , They are the extracted key points and their matching , Let's calculate directly F
// Allocate space
int ptCount = (int)m_Matches.size();
Mat p1(ptCount, 2, CV_32F);
Mat p2(ptCount, 2, CV_32F);
// hold Keypoint Convert to Mat
Point2f pt;
for (int i=0; i<ptCount; i++)
{
pt = m_LeftKey[m_Matches[i].queryIdx].pt;
p1.at<float>(i, 0) = pt.x;
p1.at<float>(i, 1) = pt.y;
pt = m_RightKey[m_Matches[i].trainIdx].pt;
p2.at<float>(i, 0) = pt.x;
p2.at<float>(i, 1) = pt.y;
}
// use RANSAC Method to calculate F
// Mat m_Fundamental;
// The above variable is the basic matrix
// vector<uchar> m_RANSACStatus;
// The above variable has been defined , Used to store RANSAC Status of each point after
m_Fundamental = findFundamentalMat(p1, p2, m_RANSACStatus, FM_RANSAC);
// Calculate the number of wild points
int OutlinerCount = 0;
for (int i=0; i<ptCount; i++)
{
if (m_RANSACStatus[i] == 0) // Status as 0 Indicates wild dot
{
OutlinerCount++;
}
}
// Calculate the interior point
// vector<Point2f> m_LeftInlier;
// vector<Point2f> m_RightInlier;
// vector<DMatch> m_InlierMatches;
// The above three variables are used to save the interior point and the matching relationship
int InlinerCount = ptCount - OutlinerCount;
m_InlierMatches.resize(InlinerCount);
m_LeftInlier.resize(InlinerCount);
m_RightInlier.resize(InlinerCount);
InlinerCount = 0;
for (int i=0; i<ptCount; i++)
{
if (m_RANSACStatus[i] != 0)
{
m_LeftInlier[InlinerCount].x = p1.at<float>(i, 0);
m_LeftInlier[InlinerCount].y = p1.at<float>(i, 1);
m_RightInlier[InlinerCount].x = p2.at<float>(i, 0);
m_RightInlier[InlinerCount].y = p2.at<float>(i, 1);
m_InlierMatches[InlinerCount].queryIdx = InlinerCount;
m_InlierMatches[InlinerCount].trainIdx = InlinerCount;
InlinerCount++;
}
}
// Convert the interior point to drawMatches Formats that can be used
vector<KeyPoint> key1(InlinerCount);
vector<KeyPoint> key2(InlinerCount);
KeyPoint::convert(m_LeftInlier, key1);
KeyPoint::convert(m_RightInlier, key2);
// Show calculation F Later interior point matching
// Mat m_matLeftImage;
// Mat m_matRightImage;
// The above two variables save the left and right images
Mat OutImage;
drawMatches(m_matLeftImage, key1, m_matRightImage, key2, m_InlierMatches, OutImage);
cvNamedWindow( "Match features", 1);
cvShowImage("Match features", &(IplImage(OutImage)));
cvWaitKey( 0 );
cvDestroyWindow( "Match features" );
————————————————
Copyright notice : This paper is about CSDN Blogger 「sunshine_guoqiang」 The original article of , follow CC 4.0 BY-SA Copyright agreement , For reprint, please attach the original source link and this statement .
Link to the original text :https://blog.csdn.net/Darlingqiang/article/details/79775542
边栏推荐
- RANSAC的代码和原理
- Market status and development prospect forecast of global active quality control air sampler industry in 2022
- MySQL读取Binlog日志常见错误和解决方法
- Hikvision Tools Manager海康威视工具大全(含SADP、录像容量计算等工具)百万安防从业者的实用工具
- 中国工业软件市场研究报告出炉,力控SCADA、MES丰富国产工业软件生态
- Minmei new energy rushes to Shenzhen Stock Exchange: the annual accounts receivable exceeds 600million and the proposed fund-raising is 450million
- Hikvision tools manager Hikvision tools collection (including sadp, video capacity calculation and other tools) a practical tool for millions of security practitioners
- 阿里巴巴的使命、愿景、核心价值观
- What is ICMP? What is the relationship between Ping and ICMP?
- 国产数据库认证考试指南汇总(2022年6月16日更新)
猜你喜欢

昱琛航空IPO被终止:曾拟募资5亿 郭峥为大股东

一种朴素的消失点计算方法

Bit.Store:熊市漫漫,稳定Staking产品或成主旋律

Vscode suggests that you enable gopls. What exactly is it?

New Zhongda chongci scientific and Technological Innovation Board: annual revenue of 284million and proposed fund-raising of 557million

破解仓储难题?WMS仓储管理系统解决方案

binder hwbinder vndbinder

MySQL中的行转列和列转行

技术分享 | kubernetes pod 简介

原创 | 2025实现“5个1”奋斗目标!解放动力全系自主非道路国四产品正式发布
随机推荐
基于STM32F103ZET6库函数跑马灯实验
云笔记到底哪家强 -- 教你搭建自己的网盘服务器
Current market situation and development prospect forecast of global 3,3 ', 4,4' - biphenyltetracarboxylic dianhydride industry in 2022
CDGA|交通行业做好数字化转型的核心是什么?
Current market situation and development prospect forecast of the global ductless heating, ventilation and air conditioning system industry in 2022
阿里巴巴的使命、愿景、核心价值观
Market status and development prospect of resorcinol derivatives for skin products in the world in 2022
Why migrate from opentsdb to tdengine
Market status and development prospect forecast of global aircraft hose industry in 2022
新中大冲刺科创板:年营收2.84亿 拟募资5.57亿
国产数据库认证考试指南汇总(2022年6月16日更新)
Row to column and column to row in MySQL
[webinar] mongodb and Google cloud accelerate enterprise digital innovation
Market status and development prospect forecast of global functional polyethylene glycol (PEG) industry in 2022
2022年信创行业空间测算
【网络研讨会】MongoDB 携手 Google Cloud 加速企业数字化创新
The difficulty of realizing time series database "far surpassing" general database in specific scenarios
工作流自动化 低代码是关键
Market status and development prospect forecast of the global shuttleless air jet loom industry in 2022
How to arrange digital collections on online platforms such as reading and Chinese online? Will "read/write-to-earn" products be launched in the future?