http://www.voidcn.com/blog/qq535033459/article/p-1939538.html

车道线检测是辅助驾驶和自动驾驶的重要研究内容,相关的算法研究已经延续了20多年,本系列文章旨在帮助初学者在阅读文献之余快速上手构建自己的项目。OpenCV极大地方便了机器视觉应用的编写,为了能在刚起步时就看得远些,我们需要站在OpenCV巨人的肩膀上。

先看一个比较简单的程序,Github地址

框架

  • class LaneDetect封装核心算法
  • void makeFromVid(string path) 根据视频文件目录读入,对视频中的图像帧进行处理
  • 主函数 调用makeFromVid

核心算法

整个算法的核心在blobRemoval函数中:

  1. findContours函数提取轮廓
  2. 对找到的轮廓进行筛选: 
    1. contourArea轮廓足够大(高于设定的阈值minSize)
    2. minAreaRect拟合矩形的有足够长的边(高于设定的阈值longLane )
    3. 根据矩形的偏角和位置再筛选
  3. 绘制满足条件的轮廓

这个算法非常简单,效果较差。属于利用车道线的基本几何特征和位置的算法。

完整源程序

/*TODO * improve edge linking * remove blobs whose axis direction doesnt point towards vanishing pt * Parallelisation * lane prediction */#include <opencv2/core/core.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <math.h>
#include <time.h>using namespace std;
using namespace cv;
clock_t start, stop;class LaneDetect
{
public:Mat currFrame; //stores the upcoming frameMat temp;      //stores intermediate resultsMat temp2;     //stores the final lane segmentsint diff, diffL, diffR;int laneWidth;int diffThreshTop;int diffThreshLow;int ROIrows;int vertical_left;int vertical_right;int vertical_top;int smallLaneArea;int longLane;int  vanishingPt;float maxLaneWidth;//to store various blob propertiesMat binary_image; //used for blob removalint minSize;int ratio;float  contour_area;float blob_angle_deg;float bounding_width;float bounding_length;Size2f sz;vector< vector<Point> > contours;vector<Vec4i> hierarchy;RotatedRect rotated_rect;LaneDetect(Mat startFrame){//currFrame = startFrame; //if image has to be processed at original sizecurrFrame = Mat(320,480,CV_8UC1,0.0);                        //initialised the image size to 320x480resize(startFrame, currFrame, currFrame.size());             // resize the input to required sizetemp      = Mat(currFrame.rows, currFrame.cols, CV_8UC1,0.0);//stores possible lane markingstemp2     = Mat(currFrame.rows, currFrame.cols, CV_8UC1,0.0);//stores finally selected lane marksvanishingPt    = currFrame.rows/2;                           //for simplicity right nowROIrows        = currFrame.rows - vanishingPt;               //rows in region of interestminSize        = 0.00015 * (currFrame.cols*currFrame.rows);  //min size of any region to be selected as lanemaxLaneWidth   = 0.025 * currFrame.cols;                     //approximate max lane width based on image sizesmallLaneArea  = 7 * minSize;longLane       = 0.3 * currFrame.rows;ratio          = 4;//these mark the possible ROI for vertical lane segments and to filter vehicle glarevertical_left  = 2*currFrame.cols/5;vertical_right = 3*currFrame.cols/5;vertical_top   = 2*currFrame.rows/3;namedWindow("lane",2);namedWindow("midstep", 2);namedWindow("currframe", 2);namedWindow("laneBlobs",2);getLane();}void updateSensitivity(){int total=0, average =0;for(int i= vanishingPt; i<currFrame.rows; i++)for(int j= 0 ; j<currFrame.cols; j++)total += currFrame.at<uchar>(i,j);average = total/(ROIrows*currFrame.cols);cout<<"average : "<<average<<endl;}void getLane(){//medianBlur(currFrame, currFrame,5 );// updateSensitivity();//ROI = bottom halffor(int i=vanishingPt; i<currFrame.rows; i++)for(int j=0; j<currFrame.cols; j++){temp.at<uchar>(i,j)    = 0;temp2.at<uchar>(i,j)   = 0;}imshow("currframe", currFrame);blobRemoval();}void markLane(){for(int i=vanishingPt; i<currFrame.rows; i++){//IF COLOUR IMAGE IS GIVEN then additional check can be done// lane markings RGB values will be nearly same to each other(i.e without any hue)//min lane width is taken to be 5laneWidth =5+ maxLaneWidth*(i-vanishingPt)/ROIrows;for(int j=laneWidth; j<currFrame.cols- laneWidth; j++){diffL = currFrame.at<uchar>(i,j) - currFrame.at<uchar>(i,j-laneWidth);diffR = currFrame.at<uchar>(i,j) - currFrame.at<uchar>(i,j+laneWidth);diff  =  diffL + diffR - abs(diffL-diffR);//1 right bit shifts to make it 0.5 timesdiffThreshLow = currFrame.at<uchar>(i,j)>>1;//diffThreshTop = 1.2*currFrame.at<uchar>(i,j);//both left and right differences can be made to contribute//at least by certain threshold (which is >0 right now)//total minimum Diff should be atleast more than 5 to avoid noiseif (diffL>0 && diffR >0 && diff>5)if(diff>=diffThreshLow /*&& diff<= diffThreshTop*/ )temp.at<uchar>(i,j)=255;}}}void blobRemoval(){markLane();// find all contours in the binary imagetemp.copyTo(binary_image);findContours(binary_image, contours,hierarchy, CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE);// for removing invalid blobsif (!contours.empty()){for (size_t i=0; i<contours.size(); ++i){//====conditions for removing contours====//contour_area = contourArea(contours[i]) ;//blob size should not be less than lower thresholdif(contour_area > minSize){rotated_rect    = minAreaRect(contours[i]);sz              = rotated_rect.size;bounding_width  = sz.width;bounding_length = sz.height;//openCV selects length and width based on their orientation//so angle needs to be adjusted accordinglyblob_angle_deg = rotated_rect.angle;if (bounding_width < bounding_length)blob_angle_deg = 90 + blob_angle_deg;//if such big line has been detected then it has to be a (curved or a normal)laneif(bounding_length>longLane || bounding_width >longLane){drawContours(currFrame, contours,i, Scalar(255), CV_FILLED, 8);drawContours(temp2, contours,i, Scalar(255), CV_FILLED, 8);}//angle of orientation of blob should not be near horizontal or vertical//vertical blobs are allowed only near center-bottom region, where centre lane mark is present//length:width >= ratio for valid line segments//if area is very small then ratio limits are compensatedelse if ((blob_angle_deg <-10 || blob_angle_deg >-10 ) &&((blob_angle_deg > -70 && blob_angle_deg < 70 ) ||(rotated_rect.center.y > vertical_top &&rotated_rect.center.x > vertical_left && rotated_rect.center.x < vertical_right))){if ((bounding_length/bounding_width)>=ratio || (bounding_width/bounding_length)>=ratio||(contour_area< smallLaneArea &&  ((contour_area/(bounding_width*bounding_length)) > .75) &&((bounding_length/bounding_width)>=2 || (bounding_width/bounding_length)>=2))){drawContours(currFrame, contours,i, Scalar(255), CV_FILLED, 8);drawContours(temp2, contours,i, Scalar(255), CV_FILLED, 8);}}}}}imshow("midstep", temp);imshow("laneBlobs", temp2);imshow("lane",currFrame);}void nextFrame(Mat &nxt){//currFrame = nxt; //if processing is to be done at original sizeresize(nxt ,currFrame, currFrame.size()); //resizing the input image for faster processinggetLane();}Mat getResult(){return temp2;}};//end of class LaneDetectvoid makeFromVid(string path)
{Mat frame;VideoCapture cap(path); // open the video file for readingif ( !cap.isOpened() )  // if not success, exit programcout << "Cannot open the video file" << endl;//cap.set(CV_CAP_PROP_POS_MSEC, 300); //start the video at 300msdouble fps = cap.get(CV_CAP_PROP_FPS); //get the frames per seconds of the videocout << "Input video's Frame per seconds : " << fps << endl;cap.read(frame);LaneDetect detect(frame);while(1){bool bSuccess = cap.read(frame); // read a new frame from videoif (!bSuccess)                   //if not success, break loop{cout << "Cannot read the frame from video file" << endl;break;}cvtColor(frame, frame, CV_BGR2GRAY);//start = clock();detect.nextFrame(frame);//stop =clock();// cout<<"fps : "<<1.0/(((double)(stop-start))/ CLOCKS_PER_SEC)<<endl;if(waitKey(10) == 27) //wait for 'esc' key press for 10 ms. If 'esc' key is pressed, break loop{cout<<"video paused!, press q to quit, any other key to continue"<<endl;if(waitKey(0) == 'q'){cout << "terminated by user" << endl;break;}}}
}int main()
{makeFromVid("/home/yash/opencv-2.4.10/programs/output.avi");// makeFromVid("/home/yash/opencv-2.4.10/programs/road.m4v");waitKey(0);destroyAllWindows();
}

本节分析一个国人开发的简单的车道检测程序(不涉及跟踪)

  • simple_lane_tracking 源码地址
  • 作者主页

概述

  • 采用opencv2编写 C++
  • 算法核心步骤 
    1. 提取车道标记特征,封装在laneExtraction类中
    2. 车道建模(两条边,单车道),封装在laneModeling类中
  • 对于断断续续的斑点车道标记(虚线)使用RANSAC匹配直线,对每幅图像,检测结果可能是感兴趣的左右车道都检测到或者全都没检测到

主程序框架

track.cpp 
主程序依次读入源文件夹下的图片,进行处理后输出到目标文件夹。由于图片上有固定的遮挡物,所以添加了mask进行去除。 
构建laneExtraction类的实例,设置参数路宽,添加mask去除固定遮挡物的干扰,然后调用getLaneCandidates得到备选车道线。

/* * extract line segments */aps::laneExtraction le;le.setLaneWidth(15);le.compute(img_org, carmask / 255);std::vector<aps::line> laneSegCandidates = le.getLaneCandidates();

构建laneModeling类的实例,对备选车道线参数化后,验证左右车道是否检测到,如果检测到则标记线段。

/* * model the lane */aps::laneModeling lm;lm.compute(laneSegCandidates);std::vector<aps::line> lanes = lm.getLanes();cv::cvtColor(img_org, img_org, CV_GRAY2BGR);ofs << fname << ",";if (lm.verifyLanes()){cv::line(img_org, lanes[0].getEndPoint1(), lanes[0].getEndPoint2(),cv::Scalar(0, 255, 255), 5, CV_AA);cv::line(img_org, lanes[1].getEndPoint1(), lanes[1].getEndPoint2(),cv::Scalar(0, 255, 255), 5, CV_AA);int x_left =(lanes[0].getEndPoint2().x < lanes[1].getEndPoint2().x) ?lanes[0].getEndPoint2().x :lanes[1].getEndPoint2().x;int x_right =(lanes[0].getEndPoint2().x > lanes[1].getEndPoint2().x) ?lanes[0].getEndPoint2().x :lanes[1].getEndPoint2().x;ofs << x_left << "," << x_right << std::endl;}else{ofs << "NONE,NONE\n";}

车道标记特征提取

laneExtraction.cpp

先看类的内部方法检测车道标记,输出结果是一个特征图:关键就是一个源图像点到特征图点的计算公式。

voidlaneExtraction::detectMarkers(const cv::Mat img, int tau){m_IMMarker.create(img.rows, img.cols, CV_8UC1);m_IMMarker.setTo(0);int aux = 0;for (int j = 0; j < img.rows; j++){for (int i = tau; i < img.cols - tau; i++){if (img.at<uchar>(j, i) != 0){aux = 2 * img.at<uchar>(j, i);aux += -img.at<uchar>(j, i - tau);aux += -img.at<uchar>(j, i + tau);aux += -abs((int) (img.at<uchar>(j, i - tau)- img.at<uchar>(j, i + tau)));aux = (aux < 0) ? (0) : (aux);aux = (aux > 255) ? (255) : (aux);m_IMMarker.at<uchar>(j, i) = (unsigned char) aux;}}}return;}

得到特征图后,去掉mask固定遮挡物部分,然后通过固定阈值进行二值化,得到二值图,再通过概率hough变换检测直线,最后遍历检测到的直线,如果检测到的直线偏角在某个固定范围内,就算作备选车道线。

voidlaneExtraction::compute(const cv::Mat img, const cv::Mat mask){detectMarkers(img, m_laneWidth);m_IMMarker = m_IMMarker.mul(mask);cv::threshold(m_IMMarker, m_IMMarker, 25, 255, CV_THRESH_BINARY);std::vector<cv::Vec4i> lines;cv::HoughLinesP(m_IMMarker, lines, 1, CV_PI / 180, 40, 20, 10);for (size_t i = 0; i < lines.size(); i++){cv::Vec4i l = lines[i];aps::line seg;seg.set(cv::Point(l[0], l[1]), cv::Point(l[2], l[3]));if ((seg.getOrientation() > 10 && seg.getOrientation() < 80)|| (seg.getOrientation() > 100 && seg.getOrientation() < 170)){m_laneSegCandid.push_back(seg);}}return;}

车道建模

下面看建模部分:laneModeling类 
如果检测到的直线不够2条,则返回失败。对检测到的直线通过kmeans分类方法分成两类,分类依据是方向信息。 
lines–>lanes 
收集各个类别的直线上的点并用RANSAC拟合成直线,得到最终的车道线,如果得到的lanes少于2条,则失败。

voidlaneModeling::compute(const std::vector<aps::line>& lines){int clusterCount = 2;if (lines.empty() || lines.size() < clusterCount){m_success = false;return;}cv::Mat points(lines.size(), 1, CV_32FC1);cv::Mat labels;cv::Mat centers(clusterCount, 1, CV_32FC1);for (size_t i = 0; i < lines.size(); i++){points.at<float>(i, 0) = lines[i].getOrientation();}/* * put all line candidates into two clusters based on their orientations */cv::kmeans(points, clusterCount, labels,cv::TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 10, 1.0),3, cv::KMEANS_PP_CENTERS, centers);for (int k = 0; k < clusterCount; k++){std::vector<cv::Point> candid_points;for (size_t i = 0; i < lines.size(); i++) // 遍历所有备选直线{if (labels.at<int>(i, 0) == k) // 如果该直线属于当前的类别K{std::vector<cv::Point> pts = lines[i].interpolatePoints(10);candid_points.insert(candid_points.end(), pts.begin(),pts.end()); // 将直线上的点收集起来,用于RANSAC}}if (candid_points.empty())continue; // 如果没有收集到,则继续看下一类/* * fit the perfect line */aps::LineModel model;aps::RansacLine2D Ransac;Ransac.setObservationSet(candid_points);Ransac.setIterations(500);Ransac.setRequiredInliers(2);Ransac.setTreshold(1);Ransac.computeModel();Ransac.getBestModel(model);double m = model.mSlope, b = model.mIntercept;if (fabs(m) <= 1e-10 && fabs(b) <= 1e-10)continue;cv::Point p1((700 - b) / (m + 1e-5), 700);cv::Point p2((1200 - b) / (m + 1e-5), 1200);aps::line laneSide;laneSide.set(p1, p2);lanes.push_back(laneSide);}if (lanes.size() < 2){m_success = false;}else{m_success = true;}return;}

对车道最后的验证步骤比较简单,如果得到的两条车道线方向近似,则失败;如果相交,则失败。laneModeling::verifyLanes()部分代码略。

线段操作的封装

封装在line类里

  • set 根据两个点的坐标指定一条线段
  • is_Set 返回线段是否已经通过set设定
  • getOrientation 返回线段方向
  • getEndPoint1,getEndPoint2返回两个端点
  • getLength 返回线段长度
  • is_Headup 端点1的纵坐标比端点2的纵坐标小?
  • getDistFromLine 线段和线段的距离
  • point2Line 点到线段的距离
  • interpolatePoints 返回线段上的点

抛物线掩码

parabolicMask用作去除图像上不必要的噪声,包括固定遮挡物和抛物线划分的ROI

/* * create the mask that can help remove unwanted noise */cv::Mat carmask = cv::imread("carmask.png", CV_LOAD_IMAGE_GRAYSCALE);aps::parabolicMask pmask(carmask.cols, carmask.rows,1.0 / carmask.rows);cv::Mat M = pmask.mkMask();M.convertTo(M, CV_8UC1);carmask.convertTo(carmask, CV_8UC1);carmask = carmask.mul(M);

RANSAC拟合直线

封装在LineModel类里面, 
关于RANSAC的讲解见 随机抽样一致性算法(RANSAC)

RANSAC是“RANdom SAmple Consensus(随机抽样一致)”的缩写。它可以从一组包含“局外点”的观测数据集中,通过迭代方式估计数学模型的参数。它是一种不确定的算法——它有一定的概率得出一个合理的结果;为了提高概率必须提高迭代次数。该算法最早由Fischler和Bolles于1981年提出。

这里不再展开。

总结

该程序的结构较好,对象设计合理,提供了一个适合扩展的车道检测框架。

下面看一个出自国外某教授的基于OpenCVSharp的车辆视觉框架,虽然包含车道检测和车辆识别,但源程序仅100行左右,非常精巧。

算法描述

检测部分的步骤很经典,是比较通用的步骤:

  1. 将图像上半部分无关内容截掉,设定ROI
  2. 转为灰度图
  3. Canny算子提取边缘
  4. Hough检测直线
  5. 通过直线角度位置信息筛选出车道线,标记到图上

效果如下图:

源程序

有博客将其中车道检测部分转为了OpenCV2,以下的源码转自该博客。原文链接

另外,国外有基于此程序进行扩展的,后续会介绍。

OpenCVSharp版本

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;using OpenCvSharp;namespace LaneDetection
{class Program{[STAThread]static void Main(){CvCapture cap = CvCapture.FromFile("test1.mp4");CvWindow w = new CvWindow("Lane Detection");CvWindow canny = new CvWindow("Canny");IplImage src, gray, dstCanny, halfFrame, smallImg;CvMemStorage storage = new CvMemStorage();CvSeq lines;while (CvWindow.WaitKey(10) < 0){src = cap.QueryFrame();halfFrame = new IplImage(new CvSize(src.Size.Width / 2, src.Size.Height / 2), BitDepth.U8, 3);Cv.PyrDown(src, halfFrame, CvFilter.Gaussian5x5);gray = new IplImage(src.Size, BitDepth.U8, 1);dstCanny = new IplImage(src.Size, BitDepth.U8, 1);storage.Clear();// Crop off top half of image since we're only interested in the lower portion of the videoint halfWidth = src.Width / 2;int halfHeight = src.Height / 2;int startX = halfWidth - (halfWidth / 2);src.SetROI(new CvRect(0, halfHeight - 0, src.Width - 1, src.Height - 1));gray.SetROI(src.GetROI());dstCanny.SetROI(src.GetROI());src.CvtColor(gray, ColorConversion.BgrToGray);Cv.Smooth(gray, gray, SmoothType.Gaussian, 5, 5);Cv.Canny(gray, dstCanny, 50, 200, ApertureSize.Size3);canny.Image = dstCanny;storage.Clear();lines = dstCanny.HoughLines2(storage, HoughLinesMethod.Probabilistic, 1, Math.PI / 180, 50, 50, 100);for (int i = 0; i < lines.Total; i++){CvLineSegmentPoint elem = lines.GetSeqElem<CvLineSegmentPoint>(i).Value;int dx = elem.P2.X - elem.P1.X;int dy = elem.P2.Y - elem.P1.Y;double angle = Math.Atan2(dy, dx) * 180 / Math.PI;if (Math.Abs(angle) <= 10)continue;if (elem.P1.Y > elem.P2.Y + 50  || elem.P1.Y < elem.P2.Y -50 ){src.Line(elem.P1, elem.P2, CvColor.Red, 2, LineType.AntiAlias, 0);}}src.ResetROI();storage.Clear();w.Image = src;}}}
}

OpenCV2版本

#include "stdafx.h"
#include <highgui.h>
#include <cv.h>
#include <math.h>using namespace cv;
using namespace std;#define INF 99999999
CvCapture* g_capture = NULL;int g_slider_pos = 0 ;
int frame_count = 0;
CvSeq* lines;int main(int argc,char* argv[])
{                  cvNamedWindow( "show");      g_capture=cvCreateFileCapture( "D:\\road.avi");IplImage* frame;while(1){  CvMemStorage* storage = cvCreateMemStorage();frame=cvQueryFrame(g_capture);//set the ROI of the original imageint x = 0,y = frame->height/2;int width = frame->width,height = frame->height/2;if(!frame) break; cvSetImageROI(frame,cvRect(x,y,width,height));IplImage* gray = cvCreateImage(cvGetSize(frame),8,1);cvCvtColor(frame,gray,CV_BGR2GRAY);cvCanny(gray,gray,50,100);cvShowImage("canny",gray);cvSmooth(gray,gray,CV_GAUSSIAN,3,1,0);//Houghlines = cvHoughLines2(gray,storage,CV_HOUGH_PROBABILISTIC,1,CV_PI/180,50,90,50);//select approprivate lines acoording to the slopefor (int i = 0;i < lines->total;i ++){double k =INF;CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);int dx = line[1].x - line[0].x;int dy = line[1].x - line[0].y;double angle = atan2(dy,dx) * 180 /CV_PI;if (abs(angle) <= 10)continue;if (line[0].y > line[1].y + 50 || line[0].y < line[1].y - 50){cvLine(frame,line[0],line[1],CV_RGB(255,0,0),2,CV_AA);}}cvResetImageROI(frame);     cvShowImage( "show",frame);char c = cvWaitKey(33);            if(c==27)break;} cvReleaseCapture(&g_capture);cvDestroyWindow( "show");               return 0;
}

参考资料

  • 国外教授的博客-OpenCVSharp版本 【中文翻译】
  • 国内某博客-改写OpenCV2版本

车道检测源码分析系列相关推荐

  1. Thinkphp源码分析系列–开篇

    目前国内比较流行的php框架由thinkphp,yii,Zend Framework,CodeIgniter等.一直觉得自己在php方面还是一个小学生,只会用别人的框架,自己也没有写过,当然不是自己不 ...

  2. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  3. MyBatis 源码分析系列文章合集

    1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...

  4. MyBatis 源码分析系列文章导读

    1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...

  5. Spring IOC 容器源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  6. Spring IOC 容器源码分析系列文章导读 1

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  7. dubbo源码分析系列(1)扩展机制的实现

    1 系列目录 dubbo源码分析系列(1)扩展机制的实现 dubbo源码分析系列(2)服务的发布 dubbo源码分析系列(3)服务的引用 dubbo源码分析系列(4)dubbo通信设计 2 SPI扩展 ...

  8. idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(一)

    课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...

  9. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

最新文章

  1. java sdf.parse_用DateFormat的parse方法验证字符串是否是日期的问题
  2. matlab程序崩溃怎么解决,java – 如何避免MATLAB崩溃打开太多的数字?
  3. vscode c++编译报错:‘nullptr’ was not declared in this scope(-std=c++11)
  4. Delphi Qjson
  5. android快速打包工具下载,【Android】多渠道打包,其实可以更快
  6. 基于Spring读写分离
  7. centos5.3安装vmware
  8. 左对齐杨辉三角python_python实现杨辉三角思路
  9. 随想录(学习消费类soc芯片)
  10. ImportError: DLL load failed: 找不到指定的模块。Import tensorflow时
  11. JAVA获取同一路径下所有子类或接口实现类
  12. android wifi列表轮询,(八十八)Android O WiFi启动流程梳理续——connectToSupplicant
  13. 434.字符串中的单词数
  14. cs6导入库闪退 flash_flash cs6源文件怎么修复,导入老跳出意外格式,我是用flash cs6做的。我还有一半没有导出 swf 呢?...
  15. Websockets 介绍和应用
  16. ZPL条码及参数说明
  17. 2018百度之星初赛(A) 1003、1002、1001
  18. AntV使用方法详解
  19. php动态创建变量名,PHP中动态创建变量名(可变变量)
  20. PMOS双电源供电隔离电路(USB接入为电池充电 电池断开供电)

热门文章

  1. open session and Hibernate事务处理机制
  2. 全1的向量与行向量或列向量相乘,实质上是作行复制或列复制!
  3. 2 时间格式化_U盘无法格式化怎么办 U盘无法格式化解决教程【详解】
  4. python脚本下载sentinel数据_Python API sentinelsat下载错误
  5. TCP与UDP之间的区别
  6. leetcode算法题--重建二叉树
  7. mfc 按钮点第一下没触发消息 第二下才触发消息_34 详细干货 | 给回避型伴侣发消息,他们不回复,该怎么办?...
  8. 跨平台移动应用开发迎来“大杀器”,Xamarin.Essentials正式版发布
  9. 儿童机器人慧昱科教获6000万元A轮融资,海尔资本领投
  10. 读取CSV数据的集中方式(Java和Oracle)