目录

0x01 了解霍夫变换

0x02 线检测技术

0x03 LSD快速直线检测

0x04 圆检测技术

0x05 轮廓检测


0x01 了解霍夫变换

霍夫变换是要从图像钟识别几何形状的基本图像处理方法之一。经典的霍夫变换用来检测图像中的直线,改进的霍夫变换扩展到识别任意形状的物体(椭圆、圆等等)。优势在于它不受图行旋转的影响,易于进行几何图行的快速变换。

经典的霍夫变换用于检测图像中的直线,其原理是利用坐标空间变换将两个坐标进行相应的转换,或通过直线映射到另一坐标空间的点形成的峰值。从而把检测任意形状的问题转化为统计峰值的问题。

基本原理如下:

以直线检测为例,每个像素坐标点经过空间变换都编程对直线特质有贡献的统一度量。

对于二维图像数据f(x,y),平面坐标为(x,y),极坐标为(r,θ)。从坐标轴可看出公式:

xcosθ+ysinθ=r

对于图像像素平面坐标(x,y),我们需要做的就是通过空间坐标映射关系,将图像笛卡尔坐标系转换到极坐标霍夫空间系统,这种点到曲线的映射变换称为霍夫变换。

0x02 线检测技术

在许多实际的图像处理应用中,我们可以检测图像中的轮廓部分或图形,直线检测有利于分析图像中的角度及其结构特征。

  • 霍夫线变换

    基于图像二值化的变换,利用二值化图像中的点集来确定候选直线的集合。我们设定分程f(x,p)=0来表示任意曲线,其中p为曲线的参数向量,那么利用霍夫变换进行线检测算法的步骤:

    (1)在参数p的范围内量化参数空间,将霍夫空间坐标(r,θ)初始化为0。

    (2)在阈值化后的梯度图中,对每个图像点(i,j)进行遍历,对于满足参数p,加权累计所有满足f(x,p)=0的单位S(p),则S(p) = S(p)+delta(p)。

    (3)计算当前霍夫空间的累计数组S(p)的局部最大值,那么对应的就是原始图像中f(x,p)=0的解析实现。

OpenCv算法并没有直接将曲线提取出来,而是返回相应的(r,θ)平面的局部最大值,因此要对OpenCV中的函数参数接口进一步理解分析。

OpenCV中提供了不同种类型的霍夫变换函数:

(1)标准的霍夫变换(SHT)

(2)多尺度霍夫变换(MHT)

(3)统计概率霍夫变换(PPHT)

实现SHT与MHT:

void HoughLinesP(InputArray image,       //输入图像OutputArray lines,        //输出线向量double rho,              //累计像素的距离分辨率double theta,           //累计弧度的角度分辨率int threshold,          //检测一条直线所需最少的曲线交点double minLineLength=0,   //最小的线长度double maxLineGap=0)   //最大的长度,用于线连接

lines由4个元素(x1,y1,x2,y2)组成,其中(x1,y1)和(x2,y2)为线段的终点坐标。

实现统计概率的霍夫变换线检测:

void HoughLines(InputArray image,        //二值化图像OutputArray lines,       //输出线向量double rho,              //累计像素的距离分辨率double theta,           //累计弧度的角度分辨率int threshold,          //检测一条直线所需最少的曲线交点double srn=0,         //多尺霍夫变换参数,距离分辨因子pdouble stn = 0)           //多尺度霍夫变换参数,距离分辨率θ因子

如何使用?(分别对应if0与if1)

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main( )
{cv::Mat srcImage = cv::imread("..\\images\\building.jpg", 0);if (!srcImage.data)   return -1;cv::Mat edgeMat, houghMat;// Canny边缘检测 二值图像Canny(srcImage, edgeMat, 50, 200, 3);cvtColor(edgeMat, houghMat, CV_GRAY2BGR);#if 0// 标准的霍夫变换vector<Vec2f> lines;HoughLines(edgeMat, lines, 1, CV_PI/180, 100, 0, 0 );for( size_t i = 0; i < lines.size(); i++ ){// 根据直线参数表达式绘制相应检测结果float rho = lines[i][0], theta = lines[i][1];Point pt1, pt2;double a = cos(theta), b = sin(theta);double x0 = a*rho, y0 = b*rho;pt1.x = cvRound(x0 + 1000*(-b));pt1.y = cvRound(y0 + 1000*(a));pt2.x = cvRound(x0 - 1000*(-b));pt2.y = cvRound(y0 - 1000*(a));line( houghMat, pt1, pt2, Scalar(0,0,255), 3, CV_AA);}#else// 统计概率的霍夫变换vector<Vec4i> lines;HoughLinesP(edgeMat, lines, 1, CV_PI/180, 50, 50, 10 );for( size_t i = 0; i < lines.size(); i++ ){Vec4i l = lines[i];// 绘制线检测结果line( houghMat, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, CV_AA);}#endifcv::imshow("srcImage", srcImage);cv::imshow("houghMat", houghMat);cv::waitKey();return 0;
}

0x03 LSD快速直线检测

该算法用于局部提取直线,时间复杂度较低。LSD算法通过对图像局部分析,得出直线的像素点集,再通过假设参数进行验证求解,将像素点集合与误差控制集合合并,进而自适应控制误检的数量。

检测图像中的直线最基本的思想就是检测图像中梯度变化较大的像素点的合集。LSD正是充分利用了梯度信息和行列线(level-line)来进行直线检测的。相关知识:

(1)行列线及支撑线

行列线:图像的灰度从黑到白或从白道黑剧烈变化的分割线,即梯度形成区域。

首先计算梯度图像中每个像素点行列线角度,以及生成行列线区域,对于单位向量域而言,该区域被分为若干子区域,所有向量与基准点行列线相切,连接区域包含同样或相似的容忍误差为t的角度。通过这样的运算可以确保它并不需要同样强度的梯度行列线,最后得到的效果:即使有锯齿线、混叠效应,同样可以使局部水平行线的方向大致是平行的。

每个支撑线区域都是以候选的直线分割方式对应支撑线区域最小外接矩形,同时满足相应的几何目标与其一一对应,几何形状矩形的主方向作为支撑线的主轴方向,矩形区域覆盖整个支撑线区域

(2)对齐点

在每个支撑线区域形成的矩形区域内,若该像素点形成的行列线方向角度与矩形主轴方向在容忍误差t的范围内保持一致性,则矩形区域内的像素个数n对齐点个数k都需要计算出来,然后通过相应准则来验证该矩形分割是否可作为正确的直线分割。

LSD算法并不重复计算单个行列线,行列线也会被基于其梯度大小进行排序,梯度变化分析最大的为最重要的行列线,该行列支撑线的获取将会根据其强梯度降序排列来得到。

LSD优点在于是可以根据几个参数的设置来提升不同应用场景下的检测性能,线性时间内就可以完成,但算法本身也存在局部算法的缺点。

下面将介绍LSD算法实现步骤:

  • 图像缩放

    将输入图像缩小至原图像的百分之八十,消除图像中的锯齿效应,利用高斯下采样的方式对输入图像进行操作(高斯模糊)。图像首先与高斯核卷积来平滑消除锯齿效应,然后采用降采样操作,防止其他干扰噪声。高斯核的标准差为:

  • 计算梯度

图像的梯度计算是通过2*2模板来完成的,较小模板也使得计算相对较快,同时也保证了邻域方向分布的相对独立性。对于灰度图像f(i,j),图像的梯度由下式可得:

图像梯度的幅值G及行列线的角度θ可由下式得到:

  • 这么一看这个算法跟canny中计算幅值及其方向的方式其实是一样的。。

  • 梯度排序

    梯度幅值剧烈变化的像素点区域一般是图像中较强边缘存在的区域。LSD算法中区域像素点的处理将会直接影响后续的梯度检测,中间像素一般具有最高的梯度幅值,需要将像素点梯度幅值进行从大到小排序,进而可以完成直线检测。

    LSD中实现的排序算法是伪排序算法,可在线性时间内完成。等间距均匀设置种子点,像素按照对应幅值归类道种子区域,然后利用像素映射关系对梯度幅值进行排序。

  • 梯度阈值

    梯度幅值较小的像素点区域对应图像中平坦区域,较小梯度值往往出现在平滑区域,不在关注的范围内,但是它们的存在往往会严重影响直线角度的计算。

    对平坦区域像素值计算其梯度时,很有可能出现更多的误差,所以在LSD中,是直接给定一个阈值,对于梯度幅值小于某个值,那么我们就限制其参与支撑线性区域的计算。

  • 区域生成

    在排序像素列表中选取状态为未使用的像素值作为种子点,搜索角度满足状态为未使用的点8邻域形成的区域生成支撑线区域

    邻域内得角度误差范围为t,像素将依次被添加道该区域内进行验证更新,种子点得行列线角度为lθ,那么区域得角度更新为:

* 其中lj为行列线区域角度,经验值t设置为PI/8为最优的参数。

* 矩形估计

支撑线区域的外接矩形对应于直线分割,在分割步骤实施之前,应先计算出矩形区域的**中心**,根据像素的梯度幅值,矩形区域的主轴方向将会被设置为**矩形的最小特征向量值对应的角度**。

(3)OpenCV中的LSD实现

包含于include/opencv2/imgproc.hpp。下面是cv::LineSegmentDetector所对应的类:

cv::LineSegmentDetector::compareSegment();   //绘制两组线(蓝色和红色),计算非重叠像素
cv::LineSegmentDetector::detect();          //对输入图像进行线检测
cv::LineSegmentDetector::drawSegment();     //给定图像的线分割绘画

如何使用:

#include <iostream>
#include <string>
#include "opencv2/core/core.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"using namespace std;
using namespace cv;
int main()
{cv::Mat srcImage =cv::imread("./image/cross.jpg", 0);if (!srcImage.data)return -1;cv::imshow("source", srcImage);// canny边缘检测Canny(srcImage, srcImage, 50, 200, 3);// 创建LSD检测类
#if 1Ptr<LineSegmentDetector> ls = createLineSegmentDetector(LSD_REFINE_STD);
#elsePtr<LineSegmentDetector> ls = createLineSegmentDetector(LSD_REFINE_NONE);
#endifdouble start = double(getTickCount());vector<Vec4f> lines_std;// 线检测ls->detect(srcImage, lines_std);double duration_ms = (double(getTickCount()) - start) * 1000 / getTickFrequency();std::cout << "It took " << duration_ms << " ms." << std::endl;// 绘图线检测结果Mat drawnLines(srcImage);ls->drawSegments(drawnLines, lines_std);cv::imshow("Standard refinement", drawnLines);cv::waitKey();return 0;
}

所消耗的时间确实很少,并且边界找的也是很准确。

0x04 圆检测技术

霍夫圆变换的基本原则其实跟上面差不多,直线检测中的点对应极坐标空间,在变换中被三维空间中圆坐标空间代替。对于一条直线来说,一条直线可以使用(p,θ)来确定,但是对于圆的话需要三个参数来确定。

如果按照二维霍夫线变换原理来进行解释的话,三维空间曲线中相交于一点的边缘点集较多,那么它们经过的共同圆上的像素点就越多,设定阈值进行相应判断一个圆是否被检测到,那么就需要消耗很多的时间以及计算量,大可不必。

OpenCV的解决:

霍夫梯度算法:依据圆心一定出现在圆上的每个点的模向量上,圆上点的模向量交点就是圆心的所在位置。

第一步:找到圆心,这样就可以将三维的累加平面转为二维累加平面。

第二步:根据所有候选中心的边缘非零像素对其的支持程序来确定半径。

使用:

  • 使用canny进行边缘检测,对边缘中的非零点,根据Sobel的水平和垂直方向计算其局部梯度。

  • 对线上像素点进行累计,标记边缘图像中非零像素点的位置。

  • 对像素中心距离排序。

  • 对候选中心进行边缘图像非零像素验证和候选中心距离验证。

代码:

#include <iostream>
#include <string>
#include "opencv2/core/core.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdio.h>using namespace cv;
using namespace std;
int main(int argc, char** argv)
{cv::Mat srcImage = imread("./image/circlecircle.png");if (!srcImage.data)return -1;cv::imshow("srcImage", srcImage);// 转换为灰度图像cv::Mat src_gray;cvtColor(srcImage, src_gray, COLOR_BGR2GRAY);// 高斯平滑滤波GaussianBlur(src_gray, src_gray, Size(9, 9), 2, 2);vector<Vec3f> circles;// 霍夫圆检测HoughCircles(src_gray, circles, HOUGH_GRADIENT,1, src_gray.rows / 8, 200, 100, 0, 0);// 将得到的结果绘图for (size_t i = 0; i < circles.size(); i++){Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));int radius = cvRound(circles[i][2]);// 检测圆中心circle(srcImage, center, 3, Scalar(0, 255, 0), -1, 8, 0);// 检测圆轮廓circle(srcImage, center, radius, Scalar(255, 0, 0), 3, 8, 0);}cv::imshow("HoughResult", srcImage);cv::waitKey(0);return 0;
}

对于函数HoughCircles:

void HoughCircles( InputArray image,         //输入图像8-bit灰度图OutputArray circles,  //输出圆的结果int method,             //定义检测图像中圆的方法double dp,                 //寻找圆弧圆心的累计分辨率double minDist,           //明显区分两个不同圆之间的最小距离double param1 = 100,     //canny边缘阈值上限,下限被设为上限的一半double param2 = 100,    //HOUGH_GRADIENT方法的累加器阈值,阈值越小,检测到的圈子越多int minRadius = 0,         //最小圆半径int maxRadius = 0 )     //最大圆半径

关于dp的一些解释:寻找圆弧圆心的累计分辨率,允许创建一个比输入图像分辨率低的累加器。这样做是因为有利于认为图像中存在的圆会自然降低到与图像宽高相同宽高相同数量的范畴。如果dp设置为1,则分辨率是相同的,如果设置为比1更大的值,累加器的分辨率会受影响变小(缩小为二分之一)。dp的值不可以比1小。

按照上面这么说,我把dp设置为2,确实识别精度越来越高了,连半圆都可以识别到:

那越大越好?这不是的,当我改到3的时候:

看到这会是会用了,可是对其实现的原理其实也是一知半解,那么再往深处看看把。

我们先看看我们正常表示一个圆的时候是怎么表示的:

(x-a)^2 + (y-b)^2 = r^2。想必这条公式所有人对其都不陌生,那么霍夫变换的圆检测就是在这三个参数中组成的,也就是参数a、b、r。a、b为圆心的坐标,r为圆的半径。

原则上,霍夫变换可以检测任何形状。但是复杂形状的时候,需要的参数非常多的,所以在程序实现上所需的内容空间以及运行效率上都不利于把标准霍夫变换应用于实际复杂的图形检测中。所以就有很多一些改进的霍夫变换就相继提出,它们的基本原理就是尽可能减小霍夫空间的维数。

所以我们现在使用的函数HoughCircles,也是改进的霍夫变换——21HT。这个函数将霍夫变换分为两个阶段,从而减少了霍夫空间的维数。

第一阶段用于检测圆心,第二阶段从圆心推导出半径。

检测圆心的原理是圆心是它所在圆周所有法线的交汇处,因此只要找到这个交点,即可确定圆心。这个方法与上面所说的霍夫变换的原理是一样的,它仅仅就是在二维的空间中进行查找。

检测圆半径的原理是从圆心到圆周上的任意一点的距离(即半径)是相同,只要确定一个阈值,只要相同距离的数量大于该阈值,那我们就认定该距离就是该圆心所对应的半径。这个方法需要计算半径的直方图,不使用霍夫空间。圆心和圆半径都得到了,那么再根据公式就可以得到一个圆了。

那么最后总结一下上面的步骤,21HT把标准霍夫变换的三维霍夫空间缩小为二维霍夫空间,因此无论再内存的使用上还是在运行的效率上,21HT都远远优于标准霍夫变换。但是该算法的不足就是在于圆半径的检测完全取决于圆心的检测,因此如果圆心检测出现偏差,那么圆半径的检测那就肯定出错,那么具体步骤是这样的:

第一阶段:找圆心

  • 对输入的图像进行边缘检测。

  • 计算图形的梯度,并确定圆周线,其中圆周的梯度就是它的法线。

  • 在二维霍夫空间内,绘出所有图形的梯度直线,某坐标点上累加和的值越大,说明在该点上直线相交的次数越多,也就是越有可能是圆心。

  • 在霍夫空间的4邻域内进行非最大值抑制。

  • 设定一个阈值,霍夫空间内累加和大于该阈值的点就对应于圆心。

第二阶段:检测圆半径

  • 计算某一个圆心到所有圆周线的距离,这些距离中就有该圆心所应的圆的半径值,这些半径值当然是相等的,并且这些圆半径的数量要远远大于其他距离值相等的数量。

  • 设定两个阈值,定义为最大半径和最小半径,保留距离在这两个半径之间的值。

  • 对保留下来的距离进行排序。

  • 找到距离相同的那些值,并计算相同值的数量。

  • 设定一个阈值,只有相同值得数量大于该阈值,才认为该值是该圆心对应的圆半径。

  • 每对一个圆心,需要完成以上的操作,可以得到所有的圆半径。

0x05 轮廓检测

图像中目标物体的形状检测是图像识别中重要的技术之一,可以对物体进行检测,提取特征轮廓,再头发轮廓点集的特征选择相应的算法进行处理。轮廓提取的原理是这样的:

先对源图像进行二值化,利用边缘点连接的层次差别,提取位于数据特征高的区域点集构成的集合,这部分最可能是物体的轮廓。

OpenCV中提供了函数findContours()用于对物体的轮廓进行检测:

void findContours(InputOutputArray image,            //输入图像OutputArrayOfArrays contours, //检测到的轮廓,每个轮廓都在向量中OutputArray hierarchy,         //包含图像拓扑结构的信息int mode,                      //可选获取轮廓的方法int method,                      //轮廓近似的方法Point offset=Point()          //可选的偏移量
)

mode(仅仅建立两层包含关系):CV_RETR_EXTERNAL(外轮廓)、CV_RETR_LIST(检测所有轮廓的不包含继承关系)、CV_RETR_TREE(检测所有轮廓的包含继承关系)、CV_RETR_CCOMP(检测所有轮廓)

method(轮廓近似方法):CV_CHAIN_APPROX_NONE(表示把轮廓上所有的点储存)、CV_CHAIN_APPROX_SIMPLE(表示只存储水平、垂直以及对角线的起始点)

我们可以使用一个滑动条来控制canny阈值,来看这个轮廓识别的效果,可以发现当阈值越高时轮廓找到的就越少:

#include <iostream>
#include <string>
#include "opencv2/core/core.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdio.h>
#include <stdlib.h>using namespace cv;
using namespace std;
Mat srcImage; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
//用于生成随机数
RNG rng(12345);
void thresh_callback(int, void*)
{Mat canny_output;vector<vector<Point> > contours;vector<Vec4i> hierarchy;// 用Canny算子检测边缘Canny(src_gray, canny_output, thresh, thresh * 2, 3);// 寻找轮廓findContours(canny_output, contours, hierarchy,RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));/// 绘出轮廓Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);for (int i = 0; i < contours.size(); i++){//随机生成颜色Scalar color = Scalar(rng.uniform(0, 255),rng.uniform(0, 255), rng.uniform(0, 255));drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());}// 显示轮廓结果namedWindow("Contours", WINDOW_AUTOSIZE);imshow("Contours", drawing);
}
int main()
{cv::Mat srcImage =cv::imread("./image/circlecircle.png");if (!srcImage.data)return -1;// 转成灰度并平滑cvtColor(srcImage, src_gray, COLOR_BGR2GRAY);blur(src_gray, src_gray, Size(3, 3));// 创建窗体namedWindow("srcImage", WINDOW_AUTOSIZE);imshow("srcImage", srcImage);// 滑动条控制canny阈值createTrackbar(" threth:", "srcImage", &thresh, max_thresh, thresh_callback);thresh_callback(0, 0);cv::waitKey(0);return(0);
}

这里更改的是canny找边界时所对应的高低阈值,调的是低阈值,输出的高阈值是低阈值的两倍,低阈值越高,找到的边界数目肯定是会被限制的越少,所以找到的轮廓自然而然就少了。

用来找边界,确实也是一个好东西:

好困好困,快点睡觉T T....

OpenCv入门(六)——几何检测相关推荐

  1. opencv入门:人脸检测

    人脸识别 实现人脸识别之前要先进行人脸检测,然后才能判断这个人是谁 人脸检测 预测离散值时,进行的是分类,对于只涉及两个类别的 二分任务,我们通常将其中一个类称为 正类(正样本),另一个就是负类(反类 ...

  2. 【OpenCV入门指南】第十三篇 人脸检测

    原文出处:http://blog.csdn.net/MoreWindows/article/details/8426318#t2 本篇介绍图像处理与模式识别中最热门的一个领域--人脸检测(人脸识别). ...

  3. 【OpenCV入门指南】第五篇 轮廓检测 上

    <[OpenCV入门指南]第三篇Canny边缘检测>中介绍了边缘检测,本篇介绍轮廓检测,轮廓检测的原理通俗的说就是掏空内部点,比如原图中有3*3的矩形点.那么就可以将中间的那一点去掉. 在 ...

  4. 【OpenCV入门教程之十七】OpenCV重映射 SURF特征点检测合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/30974513 作者:毛星云(浅墨) ...

  5. 【OpenCV入门指南】第五篇轮廓检测 下

    上一篇<[OpenCV入门指南]第五篇轮廓检测上>介绍了cvFindContours函数和cvDrawContours函数,并作了一个简单的使用示范.本篇将展示一个实例,让大家对轮廓检测有 ...

  6. OpenCV(六)之图像轮廓检测

    OpenCV(六)之图像轮廓检测 Contour detection系列 Contour detection-图像金字塔 图像金字塔-高斯金字塔 图像金字塔-拉普拉斯金字塔 Contour detec ...

  7. opencv入门基础(七)基于dlib进行本地图片、实时人脸检测

    opencv入门基础(七)基于dlib进行本地图片.实时人脸检测 一.背景知识 1.Dlib是一个深度学习开源工具,基于C++开发,也支持Python开发接口. 2.由于Dlib对于人脸特征提取效果很 ...

  8. OpenCV Java入门六 使用神经网算法辩识人脸

    介绍 前面几章我们积累了足够的opencv使用技巧.这一章我们就会进入最激动人心的辩识人脸.我们知道识脸和辩识人脸有着本质的区别. 识脸:这是一个人的脸. 辩识人脸:这是谁的脸,这才是我们需要的. 我 ...

  9. 【AI白身境】搞计算机视觉必备的OpenCV入门基础

    文章首发于微信公众号<有三AI> [AI白身境]搞计算机视觉必备的OpenCV入门基础 今天是新专栏<AI白身境>的第五篇. 曾经看过一个视频,树莓派自平衡机器人自动追着小球跑 ...

  10. 【OpenCV入门教程之十四】OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/26977557 作者:毛星云(浅墨) ...

最新文章

  1. MEET 2021 | 人工智能产业、科研、投资届大咖齐聚,共探智能科技新机会
  2. Win7环境下IPython Notebook的安装
  3. 硬刚 Kafka,Apache 顶级项目背后的公司完成数百万美元 Pre-A 轮融资
  4. ctf xor题_CTF下的命令执行
  5. Comparable与Comparator异同
  6. 15分钟搞定OLAP查询引擎Phoenix
  7. python基于django的学生在线考试自动阅卷系统(含错题本功能)
  8. R语言本地安装包教程
  9. 二叉树前中后序遍历(循环实现)
  10. 商业数据库之死:Oracle 的困境
  11. 手机投屏不是全屏怎么办_一招搞定手机投屏不是全屏问题,手机投屏自适应全屏...
  12. 《如何撰写和发表SCI期刊论文》阅读笔记
  13. dell 台式电脑设置每天定时开机和关机
  14. 喜马拉雅自研网关架构演进过程
  15. 伦敦国王学院计算机申请要求,伦敦大学国王学院计算机科学与管理本科申请条件.pdf...
  16. 虚拟服务器 共享打印机,教你轻松解决打印机共享难题
  17. 2kids学汉字 android,新2Kids学汉字
  18. 大疆自动驾驶,首次官宣即交货
  19. c语言滚动字幕的原理编程,c#中通过Graphics.DrawString实现滚动字幕的原理和代码实例...
  20. 二次元动漫卡通风格手机APP应用下载页自适应源码

热门文章

  1. 【Facebook的UI开发框架React入门之八】Image的使用简单介绍(iOS平台)-goodmao
  2. 苹果app商品定价_苹果App新定价 中国区最低或变8元
  3. 1. 查询没有选修1号课程的学生姓名。
  4. 怎样重启计算机来关闭u口,如何禁止别人用U盘拷贝文件
  5. 谷歌浏览器上的收藏网址到另一台电脑上
  6. 公司邮箱格式申请注册哪个好?
  7. vclient未知连接错误_iphone未知错误怎么办 itunes未知错误6解决方法【详解】
  8. 服务器风扇状态,服务器用风扇转速
  9. 黄金代理协议是什么?
  10. android启动windows7,WIN7 32位系统里安装和配置 Android Studio - 20130604