IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 【python OpenCV3.3 图像处理教程:直线检测、圆检测、对象测量、腐蚀、膨胀等形态学操作、数字验证码识别、人脸检测 -> 正文阅读

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

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 = 75
    dst = 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 = 55
    dst=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 = 55
    dst=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 operation
    kernel = 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 transform
    dist = 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转为int
    unknown = cv.subtract(sure_bg, surface_fg)
    ret, markers = cv.connectedComponents(surface_fg)#retval, labels
    print("ret =", ret)

    # watershed transform
    markers = markers +1
    markers[unknown == 255] = 0
    markers = 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: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)
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: #ESC
        break

在这里插入图片描述

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-04-28 11:50:53  更:2022-04-28 11:53:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 8:22:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码