1. 直线检测

Hough Line Transform:前提:边缘检测已经完成,基于霍夫变换

1.1 原理


可以通过(theta,r)唯一表示一个点。
把过三个点的全部直线以某一角度全部计算出来,如果三个点的直线有相同的,则说明有一条直线过了这三个点。

至于为啥用(theta, r)而不是斜率k和截距b来表示一条直线,是因为利用y = kx + b来表示直线时,存在斜率k无穷大的情况,无法计算。并且theta为0到2*pi, 且对于直线来说r一定小于等于b,所以计算的数值也相对较小。
参考博客

def line_detection(image):blurred = cv.GaussianBlur(image, (3, 3), 0)gray =cv.cvtColor(image,cv.COLOR_BGR2GRAY)# edges=cv.Canny(gray,50,150,apertureSize=3) #apertureSize类似于步长# edges=cv.Canny(blurred,50,150,apertureSize=3) #apertureSize类似于步长edges=cv.Canny(image,50,150,apertureSize=3) #apertureSize类似于步长lines =cv.HoughLines(edges,1,np.pi/180,200)#200:threshold,累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。# 大于阈值threshold的线段才可以被检测通过并返回到结果中。# ---------------------------# 自己计算过程如下:for line in lines:rho,theta=line[0] #r,角度 霍夫空间a=np.cos(theta) #余弦值b=np.sin(theta)x0=a*rho #这个点的x坐标y0=b*rho #这个点的y坐标# x1 stores the rounded off value of (rcos(theta)-1000sin(theta))x1 = int(x0 + 1000 * (-b))  #其实就是x0,y0这个点按比例增加或者减少一段距离,还在这条直线上的点x1,y1# y1 stores the rounded off value of (rsin(theta)+1000cos(theta))y1 = int(y0 + 1000 * (a))# x2 stores the rounded off value of (rcos(theta)+1000sin(theta))x2 = int(x0 - 1000 * (-b))# y2 stores the rounded off value of (rsin(theta)-1000cos(theta))y2 = int(y0 - 1000 * (a))cv.line(image,(x1,y1),(x2,y2),(0,0,255),2)cv.imshow("line detect image",image)

def line_detection_possible(image):gray =cv.cvtColor(image,cv.COLOR_BGR2GRAY)edges=cv.Canny(gray,50,150,apertureSize=3) #apertureSize类似于步长lines =cv.HoughLinesP(edges,1,np.pi/180,200,minLineLength=20,maxLineGap=5) #允许断点,可以定义最小长度for line in lines:x1,y1,x2,y2=line[0] #r,角度 霍夫空间cv.line(image,(x1,y1),(x2,y2),(0,0,255),2)cv.imshow("line detect possible image",image)

2. 圆检测

直线检测、圆检测:基于霍夫变换
霍夫圆检测对噪声比较敏感,所以要先进行滤波(去噪)
霍夫圆检测基于梯度变换:

  1. 检测边缘,发现可能的圆心
  2. 从候选圆心开始计算最佳半径大小
def detect_CIRCLES(image):dst=cv.pyrMeanShiftFiltering(image,10,100)#均值滤波# dst=cv.GaussianBlur(image,(3,3),0)#高斯滤波img=cv.cvtColor(dst,cv.COLOR_BGR2GRAY)circles=cv.HoughCircles(img,cv.HOUGH_GRADIENT,1,20,param1=50,param2=55,minRadius=0,maxRadius=0)#距圆心半径为20以内归为1个圆,param1较高的阈值传递到Canny边缘检测器,# param2它是检测阶段圆中心的累加器阈值。它越小,越多。 可能会检测到假圆圈。circles=np.uint16(np.around(circles))for i in circles[0,:]:cv.circle(image,(i[0],i[1]),i[2],[0,0,255],2)#在原图上画圆cv.circle(image,(i[0],i[1]),2,[255,0,0],2)#画圆心cv.imshow("circles:",image)cv.imshow("circles1:",dst)

3. 对象测量

  • cv.findContours():获取轮廓
  • cv.contourArea() :#计算轮廓面积
  • cv.boundingRect() :计算轮廓外接矩形
  • cv.moments() : 计算轮廓的几何矩
  • approxPolyDP(curve, epsilon, closed, approxCurve=None):多边形逼近函数
    参数curve表示输入的点集,直接使用轮廓点集contour
    参数epsilon表示指定的精度,即原始曲线与近似曲线之间的最大距离
    参数closed表示若为true,则说明近似曲线是闭合的,反之,若为false,则断开
    参数approxCurve表示输出的点集,当前点集是能最小包容指定点集的,画出来即是一个多边形
def measure_object(image):gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)  # 图像转灰度图# 将灰度图转为二值图 ret是阈值 binary是二值图ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)dst = cv.cvtColor(binary, cv.COLOR_GRAY2BGR)  # 边缘检测后再次灰度处理print('threshold value: %s' % ret)  # 把阈值打印出来cv.imshow('binary image', binary)  # 把二值图显示出来# 找目标的轮廓image, contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)for i, contour in enumerate(contours):area = cv.contourArea(contour)  # 计算轮廓的面积print('area', area)x, y, w, h = cv.boundingRect(contour)  # 计算轮廓外接矩形rate = min(w, h) / max(w, h)  # 计算长宽比print('rate', rate)mm = cv.moments(contour)  # 计算轮廓的几何矩print(type(mm))# 找轮廓的中心位置# cx = mm['m10'] / mm['m00']  #就是不对# cy = mm['m01'] / mm['m00']# 用个黄色小圆圈把几何图形的中心位置绘制出来# cv.circle(image, (np.int(cx), np.int(cy)), 3, (0, 255, 255), -1)# 用红色框框把外接矩形绘制出来cv.rectangle(dst, (x, y), (x + w, y + h), (0, 0, 255), 2)approxCurve = cv.approxPolyDP(contour, 4, True)"""approxPolyDP(curve, epsilon, closed, approxCurve=None)多边形逼近函数参数curve表示输入的点集,直接使用轮廓点集contour参数epsilon表示指定的精度,即原始曲线与近似曲线之间的最大距离参数closed表示若为true,则说明近似曲线是闭合的,反之,若为false,则断开参数approxCurve表示输出的点集,当前点集是能最小包容指定点集的,画出来即是一个多边形"""# print(approxCurve)              # 打印每个轮廓的特征点print(approxCurve.shape)  # 打印该点集的shape,第一个数是代表了点的个数,也就是边长连接逼近数if approxCurve.shape[0] >6:  # 显示对圆形的多边形逼近cv.drawContours(dst, contours, i, (0, 255, 0), 2)if approxCurve.shape[0] == 4:  # 显示对四边形的多边形逼近cv.drawContours(dst, contours, i, (0, 0, 255), 2)if approxCurve.shape[0] == 3:  # 显示对三角形的多边形逼近cv.drawContours(dst, contours, i, (255, 0, 0), 2)if approxCurve.shape[0] == 5:  # 显示对五边形的多边形逼近cv.drawContours(dst, contours, i, (255, 255, 0), 2)cv.imshow("measure-contours", dst)

4.膨胀和腐蚀

  • 膨胀:最大值替换中心像素;cv.dilate
  • 腐蚀:最小值替换中心像素 cv.erode
  • 膨胀和腐蚀支持任意形状的结构元素
  • 3x3、5x5的结构元素或者模板

废话不多说,看效果:如下图对二值图像进行腐蚀和膨胀的效果(如字面意思)

对彩色图像的腐蚀和膨胀效果直观来看其实就是变暗和变亮?

完整代码:

import cv2 as cv
import numpy as np
# 膨胀和腐蚀支持任意形状的结构元素  (膨胀:最大值替换中心像素;腐蚀:最小值替换中心像素)
# 3*3、5*5的结构元素或者模板
data_path='C:\\Users\\22852\\Desktop\\opencv\\data\\'
result_path='C:\\Users\\22852\\Desktop\\opencv\\result\\'
def erode_demo(image):#腐蚀gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)rect,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)cv.imshow("binary:", binary)# cv.imwrite("C:\\Users\\22852\\Desktop\\opencv\\result\\binary_handwriting.jpeg",binary)kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))dst = cv.erode(binary,kernel)cv.imshow("eroid:",dst)# cv.imwrite("C:\\Users\\22852\\Desktop\\opencv\\result\\erode_handwriting.jpeg:",dst)def dilate_demo(image):#膨胀gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)rect,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)cv.imshow("binary:",binary)kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))dst = cv.dilate(binary,kernel)cv.imshow("dilate:",dst)# cv.imwrite("C:\\Users\\22852\\Desktop\\opencv\\result\\dilate_handwriting.jpeg:", dst)src = cv.imread('C:\\Users\\22852\\Desktop\\opencv\\data\\handwriting.jpeg')
src1 = cv.imread('C:\\Users\\22852\\Desktop\\opencv\\data\\input.jpg')
cv.imshow("input image:", src)
cv.imshow("input image1:", src1)
kernel = cv.getStructuringElement(cv.MORPH_RECT,(5,5))
dst1 =cv.erode(src1,kernel)#对彩色图像进行腐蚀,更暗
dst2 =cv.dilate(src1,kernel)#对彩色图像进行膨胀,更亮
cv.imshow("erode image:", dst1)
cv.imshow("dilate image:", dst2)
# cv.imwrite(result_path+'erode_girl.jpg',dst1)
# cv.imwrite(result_path+'dilate_girl.jpg',dst2)
erode_demo(src)
dilate_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()

5.开闭操作

cv.morphologyEx(binary,cv.MORPH_OPEN,kernel)
cv.morphologyEx(binary,cv.MORPH_CLOSE,kernel)

  • 开操作:腐蚀+膨胀,运用在灰度图、二值图像分选中,去除小的干扰块,尽量保证其他地方无变化
  • 闭操作:膨胀+腐蚀,运用在灰度图、二值图像分选中,填充封闭区域,尽量保证其他地方无变化
  • 还可以进行 水平、垂直线提取
    开操作效果:

    闭操作效果(没有很好的素材)
def open_demo(image):#开操作dst = cv.GaussianBlur(image, (5,5), 0)  # 高斯滤波gray=cv.cvtColor(dst,cv.COLOR_BGR2GRAY)rect,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)# rect,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY_INV|cv.THRESH_OTSU)cv.imshow("binary:", binary)# cv.imwrite(result_path+"y_binary.jpg:",binary)kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))# kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE,(3,3))#圆型# kernel = cv.getStructuringElement(cv.MORPH_RECT,(1,30))#提取垂直线# kernel = cv.getStructuringElement(cv.MORPH_RECT,(45,1))#提取水平线dst = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel)#开操作cv.imshow("open:",dst)cv.imwrite(result_path+"y_open.jpg:",dst)def close_demo(image):#闭合操作dst = cv.GaussianBlur(image, (5, 5), 0)  # 高斯滤波gray=cv.cvtColor(dst,cv.COLOR_BGR2GRAY)rect,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)cv.imshow("binary:",binary)kernel = cv.getStructuringElement(cv.MORPH_RECT,(15,15))dst = cv.morphologyEx(binary,cv.MORPH_CLOSE,kernel)cv.imshow("close:",dst)# cv.imwrite("C:\\Users\\22852\\Desktop\\opencv\\result\\dilate_handwriting.jpeg:", dst)src = cv.imread('C:\\Users\\22852\\Desktop\\opencv\\data\\y.jpg')
src1 = cv.imread('C:\\Users\\22852\\Desktop\\opencv\\data\\lines.jpg')
# src1 = cv.resize(src, [600, 600])
# cv.namedWindow("input image",cv.WINDOW_AUTOSIZE)
cv.imshow("input image:", src)
# cv.imshow("input image:", src1)open_demo(src)
# close_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()

6.其他形态学操作

  • 顶帽:原图与开操作之间的差值 得到闭合图像外的杂质?
  • 黑帽:原图与闭操作之间的差值 得到原本闭合图像里没被填充的部分(杂质)?
  • 形态学梯度(基本梯度):膨胀后的图像减去腐蚀后的图像得到的梯度
  • 内梯度:原图-腐蚀后
  • 外梯度:膨胀后-原图

顶帽:

def top_hat_demo(image):gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)rect, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))dst = cv.morphologyEx(gray,cv.MORPH_TOPHAT,kernel)#对灰度图dst1 = cv.morphologyEx(binary,cv.MORPH_TOPHAT,kernel)#对二值图cimage = np.array(gray.shape, np.uint8)cimage = 75dst = cv.add(dst, cimage)cv.imshow("top_hat:",dst1)


黑帽:

ef Black_hat_demo(image):gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)rect, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)kernel = cv.getStructuringElement(cv.MORPH_RECT,(15,15))dst = cv.morphologyEx(gray,cv.MORPH_BLACKHAT,kernel)#对灰度图dst1 = cv.morphologyEx(binary,cv.MORPH_BLACKHAT,kernel)#对二值图cimage = np.array(gray.shape, np.uint8)cimage = 55dst=cv.add(dst,cimage)cv.imshow("Blackhat_hat:",dst1)


形态学梯度:

def Gradient_demo(image):gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)rect, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))dst = cv.morphologyEx(gray,cv.MORPH_GRADIENT,kernel)#对灰度图dst1 = cv.morphologyEx(binary,cv.MORPH_GRADIENT,kernel)#对二值图cimage = np.array(gray.shape, np.uint8)cimage = 55dst=cv.add(dst,cimage)cv.imshow("Gradient:",dst)

7. 分水岭算法

在该算法中,空间上相邻并且灰度值相近的像素被划分为一个区域。
分水岭算法的整个过程:

  1. 把梯度图像中的所有像素按照灰度值进行分类,并设定一个测地距离阈值。
  2. 找到灰度值最小的像素点(默认标记为灰度值最低点),让threshold从最小值开始增长,这些点为起始点。
  3. 水平面在增长的过程中,会碰到周围的邻域像素,测量这些像素到起始点(灰度值最低点)的测地距离,如果小于设定阈值,则将这些像素淹没,否则在这些像素上设置大坝,这样就对这些邻域像素进行了分类。
  4. 随着水平面越来越高,会设置更多更高的大坝,直到灰度值的最大值,所有区域都在分水岭线上相遇,这些大坝就对整个图像像素的进行了分区。

在OpenCV中,我们需要给不同区域贴上不同的标签。用大于1的整数表示我们确定为前景或对象的区域,用1表示我们确定为背景或非对象的区域,最后用0表示我们无法确定的区域。然后应用分水岭算法,我们的标记图像将被更新,更新后的标记图像的边界像素值为-1。

分水岭算法步骤:

  1. 模糊去噪
  2. 灰度、二值化图像
  3. morphology operation:开闭操作得到确定的背景区域
  4. distance transform:通过距离转换得到确定的前景区域,剩余部分为不确定区域
  5. 对确定的前景图像进行连接组件处理,得到标记图像
  6. watershed transform:cv.watershed()根据标记图像对原图像应用分水岭算法,更新标记图像

def watershed_demo(src):# 模糊操作blur = cv.pyrMeanShiftFiltering(src, 5, 100)#均值滤波# blur=cv.GaussianBlur(src,(3,3),0)#高斯滤波# 灰度、二值化图像gray = cv.cvtColor(blur, cv.COLOR_BGR2GRAY)ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)cv.imshow("binary", binary)# morphology operationkernel = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))mb = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel, iterations=2) #开操作迭代两次sure_bg = cv.morphologyEx(mb, cv.MORPH_CLOSE, kernel, iterations=3) #闭操作迭代3次cv.imshow("morphology operation", sure_bg)# distance transformdist = cv.distanceTransform(mb, cv.DIST_L2, 3) #基于L2的距离变换dist_output = cv.normalize(dist, 0, 1, cv.NORM_MINMAX) #归一化cv.imshow("distance_t", dist_output*50)ret, surface = cv.threshold(dist, dist.max()*0.6, 255, cv.THRESH_BINARY)cv.imshow("surface", surface)surface_fg = np.uint8(surface) #float转为intunknown = cv.subtract(sure_bg, surface_fg)ret, markers = cv.connectedComponents(surface_fg)#retval, labelsprint("ret =", ret)# watershed transformmarkers = markers +1markers[unknown == 255] = 0markers = cv.watershed(src, markers = markers)src[markers == -1] = [0, 0, 255]cv.imshow("result", src)

8. 案例:数字验证码识别

import cv2 as cv
import numpy as np
from PIL import Image
# import pytesseract as tess
def recognize_txt(image):gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY)rect,binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)cv.imshow("binary:", binary)kernel = cv.getStructuringElement(cv.MORPH_RECT,(2,1))binary1 = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel)cv.imshow("binary1:",binary1)kernel = cv.getStructuringElement(cv.MORPH_RECT, (1, 2))binary2 = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel)cv.imshow("binary2:", binary2)cv.bitwise_not(binary2,binary2)#要变为白色背景txt_image=Image.fromarray(binary2)text = tess.image_to_string(txt_image)print("识别结果:%s"%text)src = cv.imread("data/yzm.jpg")
cv.imshow("input image:", src)
recognize_txt(src)
cv.waitKey(0)
cv.destroyAllWindows()

9.案例:人脸检测

需要调用opencv官方的库,比如说”haarcascade_frontalface_alt_tree.xml“
把官方库下载到本地用cv.CascadeClassifier加载

import cv2 as cv
import numpy as np
def face_detect(image):gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)face_detector= cv.CascadeClassifier("C:\\Users\\22852\\Desktop\\opencv\\opencv-3.3.1\\data\\haarcascades\\haarcascade_frontalface_alt_tree.xml")faces=face_detector.detectMultiScale(gray,1.02,5)# 5:minNeighbors,越高可能检测不到人脸#1.02:scaleFactor for x,y,w,h in faces:cv.rectangle(image,(x,y),(x+w,y+h),(0,255,255),2)cv.imshow("result:",image)
src = cv.imread('C:\\Users\\22852\\Desktop\\opencv\\data\\face.jpg')
cv.namedWindow("input image:",cv.WINDOW_AUTOSIZE)
cv.imshow("input image:", src)
face_detect(src)


也可以直接调摄像头进行视频检测~

import cv2 as cv
import numpy as np
def face_detect(image):gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)face_detector= cv.CascadeClassifier("C:\\Users\\22852\\Desktop\\opencv\\opencv-3.3.1\\data\\haarcascades\\haarcascade_frontalface_alt_tree.xml")faces=face_detector.detectMultiScale(gray,1.02,5)# 5:minNeighbors,越高可能检测不到人脸#1.02:scaleFactorfor x,y,w,h in faces:cv.rectangle(image,(x,y),(x+w,y+h),(0,255,255),2)cv.imshow("result:",image)
capture = cv.VideoCapture(0)
while(True):ret,frame = capture.read()frame=cv.flip(frame,1)face_detect(frame)c=cv.waitKey(10)if c==27: #ESCbreak

【python OpenCV3.3 图像处理教程:直线检测、圆检测、对象测量、腐蚀、膨胀等形态学操作、数字验证码识别、人脸检测相关推荐

  1. Python+OpenCV3.3图像处理视频教程 贾志刚 代码笔记3

    21 直线检测 import cv2 as cv import numpy as npdef line_detection(image):gray = cv.cvtColor(image, cv.CO ...

  2. 人脸表情识别/人脸检测/ML/DL/图像处理博主

    人脸表情识别/人脸检测/ML/DL/图像处理博主 人脸表情识别/人脸检测/ML/DL/图像处理博主:https://blog.csdn.net/app_12062011/article/categor ...

  3. Python+OpenCV3.3图像处理视频教程 贾志刚 代码笔记1

    2 图像加载与保存 import cv2 as cv import numpy as npdef video_demo():capture = cv.VideoCapture(0)while(True ...

  4. Python+OpenCV3.3图像处理视频教程 贾志刚 代码笔记2

    11 边缘保留滤波(EPF) import cv2 as cv import numpy as npdef bi_demo(image):dst = cv.bilateralFilter(image, ...

  5. Python+OpenCV3.3图像处理视频教程 贾志刚1

    1.概述与环境搭建 pip install opencv-pythonpip install opencv-contrib-pythonpip install pytesseract 测试: impo ...

  6. 基于Python的KNN数字验证码识别

    一.主要内容 本项目基于Python爬虫爬取验证码图片,对图片进行去噪.分割,通过KNN算法训练模型,实现验证其准确率. 二.系统流程 首先从指定的网页中爬取验证码图片数据,然后对数据进行一个去噪和分 ...

  7. 【学习笔记】opencv的python接口 形态学操作 腐蚀 膨胀 通用形态学函数

    腐蚀 img=np.zeros((5,5),np.uint8) img[1:4,1:4]=1 kernel=np.ones((3,1),np.uint8) erosion=cv2.erode(img, ...

  8. Java 图像处理教程(人脸检测,添加水印,图像颜色转换)

    Java 图像处理教程(人脸检测,添加水印,图像颜色转换) 文章目录 Java 图像处理教程(人脸检测,添加水印,图像颜色转换) 1:图片的读和写 2:彩色图像转换成灰度图像 3:彩色图像转换成负图像 ...

  9. Python OpenCV3 计算机视觉秘籍:1~5

    原文:OpenCV 3 Computer Vision with Python Cookbook 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自[ApacheCN 计算机视觉 译文集],采 ...

最新文章

  1. 使用C#创建SQLite控制台应用程序
  2. C++ STL容器总结之vector(超详细版)
  3. boost::phoenix::arg_names::arg1用法的测试程序
  4. Sentry UDFWhiteList bug分析
  5. 160809230张钊
  6. 关于.net framework 1.1 与 .net framework 2.0的小问题
  7. C++primer 第 3 章 字符串、向量和数组 3 . 3 标准库类型vector
  8. Kubernetes理论02
  9. Django视图层:视图函数、视图类
  10. python实验项目_Python3实验 项目结构(文件操作)
  11. C++语言虚函数表实现多态原理
  12. JavaScript之语句
  13. 使用gdb调试出现 No debugging symbols found in a.out
  14. springboot使用logback
  15. ASP.NET MVC5+EF6+EasyUI 后台管理系统(53)-工作流设计-我的批阅
  16. 数独算法-递归与回溯
  17. 受损固态硬盘(SSD)数据恢复方法(福利:固态硬盘免费恢复数据)
  18. 大话设计模式(php版)第五章——依赖倒装原则
  19. 通信原理仿真100例 | 多普勒频移的matlab仿真
  20. 情人节表白代码:浪漫玫瑰花,俘获少女心

热门文章

  1. 宝石与石头(c++)
  2. 【UI设计基础知识】UI界面设计怎么划分
  3. 测试用例设计之正交法
  4. html5获得麦克风音量
  5. 一步步拆解STC32G屠龙刀示波器开源程序,边学边用。
  6. SpringBoot 2.x 集成QQ邮箱、网易系邮箱、Gmail邮箱发送邮件
  7. 解析智能硬件“独角兽”们的发展态势!附2017年全球硬件领域“独角兽”企业榜单
  8. kaggle如何使用utility script
  9. Outlook信件图片显示红叉
  10. 我的IT之路,在哪里?