环境

提示:硬件环境Mac m1
python:3.8
opencv-python: 3.4.8.29
pytesseract: 0.3.9
cnocr: 2.1.2.1


文章目录

  • 环境
  • 前言
    • 正面识别
    • 背面识别
    • 源码下载

前言

这里利用opencv 做身份证照片处理,为识别增加准确度。利用pytesseract 做文字提取,由于是识别身份证,需要提前下载好中文字体包。


正面识别

代码如下(示例):

# -*- coding: utf-8 -*-
import os, cv2
import sys, numpy as np
import math
import include.binaryzation as bz
import include.functions as func
import copy
import fileutilDEBUG = FalseCARD_NAME = ''
CARD_SEX = ''
CARD_ETHNIC = ''
CARD_YEAR = ''
CARD_MON = ''
CARD_DAY = ''
CARD_ADDR = ''
# 身份证号码
CARD_NUM = ''# from imutils.perspective import four_point_transform# parser = argparse.ArgumentParser()
# parser.add_argument('image', help='path to image file')
# args = parser.parse_args()
#
_localDir = os.path.dirname(__file__)
_curpath = os.path.normpath(os.path.join(os.getcwd(), _localDir))
curpath = _curpathdef show(image, window_name):cv2.namedWindow(window_name, 0)cv2.imshow(window_name, image)# 0任意键终止窗口cv2.waitKey(0)cv2.destroyAllWindows()def getCardNum(img, kenalRect):"""识别并提取身份证号码:return:"""gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)thr = bz.myThreshold().getMinimumThreshold(gray)ret, binary = cv2.threshold(gray, thr, 255, cv2.THRESH_BINARY)# 3. 膨胀和腐蚀操作的核函数kenal = cv2.getStructuringElement(cv2.MORPH_RECT, (kenalRect[0], kenalRect[1]))dilation = cv2.dilate(binary, kenal, iterations=1)erosion = cv2.erode(dilation, kenal, iterations=1)# OCR识别cardNum = func.ocr(erosion)cardNum = func.is_identi_number(cardNum)if cardNum:return cardNumreturn Falsedef getChineseChar(img, kenalRect):"""分析汉字区域,并识别提取:return:"""global CARD_NAMEglobal CARD_SEXglobal CARD_ETHNICglobal CARD_YEARglobal CARD_MONglobal CARD_DAYglobal CARD_ADDRglobal CARD_NUMCARD_NAME = CARD_SEX = CARD_ETHNIC = CARD_YEAR = CARD_MON = CARD_DAY = CARD_ADDR = ''# 图片大小比例缩小处理,为了加快性能h, w, _ = img.shapemin_w = 200scale = 1  # min(1., w * 1. / min_w)h_proc = int(h * 1. / scale)w_proc = int(w * 1. / scale)im_dis = cv2.resize(img, (w_proc, h_proc))# 灰度处理gray = cv2.cvtColor(im_dis, cv2.COLOR_BGR2GRAY)# 2. 形态学变换的预处理,得到可以查找矩形的图片mybz = bz.myThreshold()algos = mybz.getAlgos()for i in algos:# 获取阈值thr = getattr(mybz, algos[i])(gray)# thr = mybz.getMinimumThreshold(gray)# func.showImg(gray, 'gray')# 膨胀和腐蚀ret, binary = cv2.threshold(gray, thr, 255, cv2.THRESH_BINARY)# 获取行起始坐标boundaryCoors = func.horizontalProjection(binary)if not boundaryCoors:continue# print(boundaryCoors)# 垂直投影对行内字符进行切割erosion = cb = copy.copy(binary)# show(binary, 'binary')coors = {}  # 信息所对应的坐标textLine = 0  # 有效文本行序号for LineNum, boundaryCoor in enumerate(boundaryCoors):if textLine == 2:kenal1 = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1))kenal2 = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))dilation = cv2.dilate(cb, kenal1, iterations=1)erosion = cv2.erode(dilation, kenal2, iterations=1)vertiCoors, text = func.verticalProjection(erosion, boundaryCoor, textLine, img)if len(vertiCoors) == 0:continueif textLine == 0:CARD_NAME = textelif textLine == 1:if text[0] != '男' and text[0] != '女':CARD_SEX = func.getSexByCardNum(CARD_NUM)else:CARD_SEX = text[0]CARD_ETHNIC = text[1]elif textLine == 2:# 为了获取更加精准的值,通过身份证号码规则直接取得出生年月CARD_YEAR = text[0]CARD_MON = text[1]CARD_DAY = text[2]passelse:CARD_ADDR += textif DEBUG:fator = 2for verticoo in vertiCoors:box = [[verticoo[0] * scale - fator, boundaryCoor[0] * scale - fator],[verticoo[1] * scale + fator, boundaryCoor[0] * scale - fator],[verticoo[1] * scale + fator, boundaryCoor[1] * scale + fator],[verticoo[0] * scale - fator, boundaryCoor[1] * scale + fator],]cv2.drawContours(img, [np.int0(box)], 0, (0, 255, 0), 2)textLine += 1returnreturn Falsedef findChineseCharArea(cardNumPoint1, width, hight):"""根据身份证号码的位置推断姓名、性别、名族、出生年月、住址的位置:param cardNumPoint1: tuple 身份证号码所处的矩形的左上角坐标:param width: int 身份证号码所处的矩形的宽:param hight: int 身份证号码所处的矩形的高:return:"""# new_x = int(cardNumPoint1[0] - (width / 18) * 6)new_x = cardNumPoint1[0] - (width / 18) * 5.5new_width = int(width / 5 * 4)box = []# new_y = cardNumPoint1[1] - hight * 6.5card_hight = hight / (0.9044 - 0.7976)  # 身份证高度card_y_start = cardNumPoint1[1] - card_hight * 0.7976  # 粗略算出图像中身份证上边界的y坐标# 为了保证不丢失文字区域,姓名的相对位置保留,以身份证上边界作为起始切割点# new_y = card_y_start# + card_hight * 0.0967# 容错因子,防止矩形存在倾斜导致区域重叠factor = 20new_y = card_y_start if card_y_start > factor else factornew_hight = card_hight * (0.7616 - 0.0967) + card_hight * 0.0967# 文字下边界坐标new_y_low = (new_y + new_hight) if (new_y + new_hight) <= cardNumPoint1[1] - factor else cardNumPoint1[1] - factorbox.append([new_x, new_y])box.append([new_x + new_width, new_y])box.append([new_x + new_width, new_y_low])box.append([new_x, new_y_low])box = np.int0(box)return boxdef detect(img):global CARD_NUMCARD_NUM = ''notFound = True# 1.  转化成灰度图gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 2. 遍历二值化阈值算法algos = bz.myThreshold().getAlgos()for i in algos:# 形态学变换的预处理,得到可以查找矩形的图片dilation = preprocess(gray, algos[i])# print('dilation',dilation)# 3. 查找和筛选文字区域region = findTextRegion(dilation)# 4. 用绿线画出这些找到的轮廓angle = 0for rect in region:angle = rect[2]# 识别身份证号码a, b = rect[1]if a > b:width = ahight = bpts2 = np.float32([[0, hight], [0, 0], [width, 0], [width, hight]])else:width = bhight = aangle = 90 + anglepts2 = np.float32([[width, hight], [0, hight], [0, 0], [width, 0]])# 透视变换box = cv2.boxPoints(rect)pts1 = np.float32(box)M = cv2.getPerspectiveTransform(pts1, pts2)cropImg = cv2.warpPerspective(img, M, (int(width), int(hight)))# show(cropImg, 'cropImg')# 计算核大小kenalx = kenaly = int(math.ceil((hight / 100.0)))CARD_NUM = getCardNum(cropImg, (kenalx, kenaly))if CARD_NUM:notFound = False# 找到身份证号码,然后根据号码区域的倾斜角度,对原图进行旋转变换if abs(angle) > 10:sp = img.shapeH = sp[0]W = sp[1]M = cv2.getRotationMatrix2D((W / 2, H / 2), angle, 1)cropImg = cv2.warpAffine(img, M, (W, H))# cv2.namedWindow("倾斜矫正", cv2.WINDOW_NORMAL)# cv2.imshow("倾斜矫正", cropImg)# cv2.waitKey(0)# 矫正图片地址global curpathpath = 'tilt_correction.jpg'newFile = os.path.join(curpath, path)cv2.imwrite(newFile, cropImg)return True, '', newFile# 寻找汉字区域# 裁剪后的图片box = cv2.boxPoints(rect)box = np.int0(box)cropImg, point, width, hight = func.cropImgByBox(img, box)box = findChineseCharArea(point, width, hight)# cv2.drawContours(img, [box], 0, (0, 255, 0), 3)chiCharArea, point, width, hight = func.cropImgByBox(img, box)getChineseChar(chiCharArea, (kenalx, kenaly))# if DEBUG:#     show(cropImg,'cropImg')# winname = "身份证号码: %s" % (CARD_NUM)# cv2.namedWindow(winname, cv2.WINDOW_NORMAL)# cv2.imshow(winname, cropImg)# cv2.waitKey(0)breakif notFound:continueelse:breakif notFound:# win32api.MessageBox(0, "无法识别,请换一个分辨率高点的照片~", "错误提示")return False, '无法识别,请换一个分辨率高点的照片~", "错误提示', ''# 带轮廓的图片if DEBUG:# cv2.namedWindow("img", cv2.WINDOW_NORMAL)# cv2.imshow("img", img)key = cv2.waitKey(0)## cv2.destroyAllWindows()if key != 32:sys.exit()if CARD_NUM != '':# 为了获取更加精准的值,通过身份证号码规则直接取得出生年月CARD_YEAR, CARD_MON, CARD_DAY = func.getBirthByCardNum(CARD_NUM)if DEBUG:info = """姓名:%s性别:%s     民族:%s出生:%s 年 %s  月 %s 日住址:%s公民身份号码:%s""" % (CARD_NAME, CARD_SEX, CARD_ETHNIC, CARD_YEAR, CARD_MON, CARD_DAY, CARD_ADDR, CARD_NUM)# ret = [CARD_NAME, CARD_SEX, CARD_ETHNIC, CARD_YEAR, CARD_MON, CARD_DAY, CARD_ADDR, CARD_NUM]ret = {"card_name": CARD_NAME, "CARD_SEX": CARD_SEX, "card_ethnic": CARD_ETHNIC,"date_time": CARD_YEAR + "-" + CARD_MON + "-" + CARD_DAY, "card_addr": CARD_ADDR, "card_num": CARD_NUM}return True, ret, ''else:return True, '', ''def calculateElement(img):# 根据图片大小粗略计算腐蚀 或膨胀所需核的大小sp = img.shapewidth = sp[1]  # width(colums) of imagekenaly = math.ceil((width / 400.0) * 12)kenalx = math.ceil((kenaly / 5.0) * 4)a = (int(kenalx), int(kenaly))return adef preprocess(gray, algoFunc):# 1. Sobel算子,x方向求梯度# sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 0, ksize = 3)# 获取二值化阈值thr = bz.myThreshold()# threshold = thr.get1DMaxEntropyThreshold(gray)threshold = getattr(thr, algoFunc)(gray)if threshold <= 0:raise Exception("获取二值化阈值失败")# 2. 二值化ret, binary = cv2.threshold(gray, threshold, 255, cv2.THRESH_BINARY)# 获取核大小calculateElement(gray)# 3. 膨胀和腐蚀操作的核函数element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))element2 = cv2.getStructuringElement(cv2.MORPH_RECT, calculateElement(gray))# 微处理去掉小的噪点dilation = cv2.dilate(binary, element1, iterations=1)binary = cv2.erode(dilation, element1, iterations=1)# 文字膨胀与腐蚀使其连成一个整体erosion = cv2.erode(binary, element2, iterations=1)dilation = cv2.dilate(erosion, element1, iterations=1)# show(erosion,'erosion')# show(dilation,'dilation')# 7. 存储中间图片# cv2.namedWindow("binary", cv2.WINDOW_NORMAL)# cv2.imshow("binary", binary)# cv2.waitKey(0)## cv2.namedWindow("dilation2", cv2.WINDOW_NORMAL)# cv2.imshow("dilation2", erosion)# cv2.waitKey(0)## cv2.namedWindow("dilation2", cv2.WINDOW_NORMAL)# cv2.imshow("dilation2", dilation)# cv2.waitKey(0)cv2.destroyAllWindows()# sys.exit(0)return dilationdef findTextRegion(img):region = []# 1. 查找轮廓binary, contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)# 2. 筛选那些面积小的for i in range(len(contours)):cnt = contours[i]# 计算该轮廓的面积area = cv2.contourArea(cnt)# 面积小的都筛选掉if (area < 1000):continue# 找到最小的矩形,该矩形可能有方向rect = cv2.minAreaRect(cnt)# 计算高和宽 width = rect[1][0]hight = rect[1][1]# 筛选那些太细的矩形,留下扁的if hight > width:if hight < width * 5:continueelse:if width < hight * 5:continueregion.append(rect)return regiondef nothing(x):passdef fushiyupengzhang(pathtoimage):"""腐蚀与膨胀动态取值预览:param pathtoimage::return:"""img = cv2.imread(pathtoimage)im_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# sobel = cv2.Sobel(im_gray, cv2.CV_8U, 1, 0, ksize=3)# 获取二值化阈值thr = bz.myThreshold()threshold = thr.getMinimumThreshold(im_gray)if threshold <= 0:raise Exception("获取二值化阈值失败")retval, img = cv2.threshold(im_gray, threshold, 255, cv2.THRESH_BINARY)cv2.namedWindow('image', cv2.WINDOW_NORMAL)cv2.imshow('image', img)cv2.createTrackbar('Er/Di', 'image', 0, 1, nothing)# 创建腐蚀或膨胀选择滚动条,只有两个值cv2.createTrackbar('x', 'image', 0, 100, nothing)# 创建卷积核大小滚动条cv2.createTrackbar('y', 'image', 0, 100, nothing)while (1):s = cv2.getTrackbarPos('Er/Di', 'image')x = cv2.getTrackbarPos('x', 'image')y = cv2.getTrackbarPos('y', 'image')# 分别接收两个滚动条的数据if x == 0:x = 1if y == 0:y = 1k = cv2.waitKey(1)kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (x, y))# 根据滚动条数据确定卷积核大小erroding = cv2.erode(img, kernel, iterations=1)dilation = cv2.dilate(img, kernel, iterations=1)if k == 27:break# esc键退出if s == 0:cv2.imshow('image', erroding)else:cv2.imshow('image', dilation)# 判断是腐蚀还是膨胀def imgRotation(pathtoimg):# 图片自动旋正from PIL import Imageimg = Image.open(pathtoimg)if hasattr(img, '_getexif') and img.getexif() != None:# 获取exif信息dict_exif = img._getexif()if dict_exif.has_key(274):if dict_exif[274] == 3:# 顺时针180new_img = img.rotate(-180)new_img.save(pathtoimg)elif dict_exif[274] == 6:# 顺时针90°new_img = img.rotate(-90)new_img.save(pathtoimg)elif dict_exif[274] == 8:# 逆时针90°new_img = img.rotate(90)new_img.save(pathtoimg)return Nonedef enhanceImage(pathtoimg):from PIL import Imagefrom PIL import ImageEnhance# 原始图像image = Image.open(pathtoimg)# 对比度增强enh_con = ImageEnhance.Contrast(image)contrast = 1.5image_contrasted = enh_con.enhance(contrast)image_contrasted.show()if __name__ == '__main__':if len(sys.argv) <= 1:sys.exit("文件路径不能为空")path = sys.argv[1]path = fileutil.download_img(path)if len(path) < 1:sys.exit('文件获取失败或者文件小于50kb')pathtoimg = cv2.imread(path)try:ret, msg, path, = detect(pathtoimg)if path != '':# 读取文件img = cv2.imread(path)ret, msg, _ = detect(img)os.unlink(path)if ret:result = [{i: msg[i]} for i in range(len(msg))]print('result', result)else:print({'msg': msg})else:if ret:print({'data': msg})else:print({'msg': msg})except Exception as e:print('e', e)

背面识别

代码如下(示例):

import cv2, re, math, sys, fileutil
import pytesseract
import numpy as np
import include.binaryzation as bzDEBUG = Falsedef show(image, window_name):pass
# cv2.namedWindow(window_name, 0)
# cv2.imshow(window_name, image)
# 0任意键终止窗口
# cv2.waitKey(0)
# cv2.destroyAllWindows()def calculateElement(img):# 根据图片大小粗略计算腐蚀 或膨胀所需核的大小sp = img.shapewidth = sp[1]  # width(colums) of imagekenaly = math.ceil((width / 400.0) * 12)kenalx = math.ceil((kenaly / 5.0) * 4)a = (int(kenalx), int(kenaly))return adef preprocess(gray, algoFunc):# 1. Sobel算子,x方向求梯度# sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 0, ksize = 3)# 获取二值化阈值thr = bz.myThreshold()# threshold = thr.get1DMaxEntropyThreshold(gray)threshold = getattr(thr, algoFunc)(gray)if threshold <= 0:raise Exception("获取二值化阈值失败")# 2. 二值化ret, binary = cv2.threshold(gray, threshold, 255, cv2.THRESH_BINARY)# 获取核大小calculateElement(gray)# 3. 膨胀和腐蚀操作的核函数element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))element2 = cv2.getStructuringElement(cv2.MORPH_RECT, calculateElement(gray))# 微处理去掉小的噪点dilation = cv2.dilate(binary, element1, iterations=1)binary = cv2.erode(dilation, element1, iterations=1)# 文字膨胀与腐蚀使其连成一个整体erosion = cv2.erode(binary, element2, iterations=1)dilation = cv2.dilate(erosion, element1, iterations=1)return dilation# 缩放函数
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):dim = None(h, w) = image.shape[:2]if width is None and height is None:return imageif width is None:r = height / float(h)dim = (int(w * r), height)else:r = width / float(w)dim = (width, int(h * r))resized = cv2.resize(image, dim, interpolation=inter)return resizedif __name__ == '__main__':path = '/Users/zfh/PycharmProjects/ocr-idcard/001.jpg'if DEBUG:if len(sys.argv) <= 1:sys.exit("文件路径不能为空")path = sys.argv[1]path = fileutil.download_img(path)if path:sys.exit('文件获取失败或者文件小于50kb')pathtoimg = cv2.imread(path)img = resize(pathtoimg, width=500, height=400)# 1. 灰度处理gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)show(gray, 'gray')# 2. 遍历二值化阈值算法blur = cv2.medianBlur(gray, 1)show(blur, 'blur')# 2. 遍历二值化阈值算法algos = bz.myThreshold().getAlgos()for i in algos:thr = bz.myThreshold()threshold_val = getattr(thr, algos[i])(blur)if threshold_val <= 0:continue# 2. 二值化threshold = cv2.threshold(blur, threshold_val, 255, cv2.THRESH_TRUNC)[1]show(threshold, 'threshold')canny = cv2.Canny(threshold, 100, 150)show(canny, 'canny')kernel = np.ones((3, 3), np.uint8)dilate = cv2.dilate(canny, kernel, iterations=5)show(dilate, "dilate")width = threshold.shape[0]height = threshold.shape[1]if width > height:threshold = np.rot90(threshold)show(threshold, 'resize1')result = pytesseract.image_to_string(threshold, lang='chi_sim+eng')ret = re.findall(r"\d+", result)if len(ret[0]) != 4:continueif len(ret[1]) != 2:continueif len(ret[2]) != 2:continueif len(ret[3]) != 4:continuestart_time = '.'.join(str(i) for i in ret[0:3])end_time = start_time.replace(ret[0], ret[3])print(start_time + "-" + end_time)break

源码下载

https://download.csdn.net/download/weixin_44774716/85761531

python opencv 身份证照片识别,包含正面、反面(附完整代码,可直接使用)相关推荐

  1. opengl实现三维动画简单代码_使用Python简单实现马赛克拼图!内附完整代码

    今天小编带大家使用python简单实现马赛克拼图,内容比以往会稍长一些,各位看官老爷可以慢慢细读,若有不足之处还望请斧正,闲话不多说,请看文章. 先看原图: 效果图: 思路: 拼图的原理其实很简单,就 ...

  2. 基于python nlp PyTorch智能对联生成系统 附完整代码 毕业设计

    软件标题:智能对联生成系统 b 系统概述 使用项目:智能对联生成系统 软件用途:通过网页端可以获取到根据已有上联只能生成的下联. 开发历史:本项目未曾有前置版本.但在服务器搭建,Tensorflow ...

  3. Python实现飞机大战游戏(后附完整代码文件和素材)

    前言 自学Python的记录,不喜勿喷! 本程序的代码是以B站黑马程序员的视频代码为基础,个人加以总结思考,写就的博文,纯属记录自己的学习历程! 当初想到要用Python的pygame模块开发飞机大战 ...

  4. 基于python图书馆管理系统和读者系统(附完整代码以及打包好的exe文件)

    摘要: 本文基于python的图书馆管理系统和读者系统,实现了登录.注册.忘记密码.书籍查询.借阅.归还.修改等功能,通过csv文件将数据存储在本地.注册时采用了邮箱验证码,模拟了现实场景.(全部源代 ...

  5. OpenCV编写视频文件的实例(附完整代码)

    OpenCV编写视频文件的实例 OpenCV编写视频文件的实例 OpenCV编写视频文件的实例 #include <iostream> // for standard I/O #inclu ...

  6. OpenCV读取视频文件的实例(附完整代码)

    OpenCV读取视频文件的实例 OpenCV读取视频文件的实例 OpenCV读取视频文件的实例 #include <iostream> // for standard I/O #inclu ...

  7. OpenCV SURF FLANN匹配的实例(附完整代码)

    OpenCV SURF FLANN匹配的实例 OpenCV SURF FLANN匹配的实例 OpenCV SURF FLANN匹配的实例 #include <iostream> #incl ...

  8. OpenCV使用Sobel或Scharr OpenCV函数进行边缘检测的实例(附完整代码)

    OpenCV使用Sobel或Scharr OpenCV函数进行边缘检测的实例 OpenCV使用Sobel或Scharr OpenCV函数进行边缘检测的实例 OpenCV使用Sobel或Scharr O ...

  9. OpenCV重新映射Remap的实例(附完整代码)

    OpenCV重新映射Remap的实例 OpenCV重新映射Remap的实例 OpenCV重新映射Remap的实例 #include "opencv2/imgcodecs.hpp" ...

  10. OpenCV Lucas-Kanade光流计算的实例(附完整代码)

    OpenCV Lucas-Kanade光流计算的实例 OpenCV Lucas-Kanade光流计算的实例 OpenCV Lucas-Kanade光流计算的实例 #include <iostre ...

最新文章

  1. centos7下Gitlab+Jenkins部署持续集成CI环境
  2. sp_updatestats和update statistics的区别
  3. TC字符界面-菜单程序【原创】
  4. Avalonia跨平台入门第十五篇之ListBox聊天窗口
  5. 计算机辅助审计学心得,审计学心得体会2000字
  6. 【python】numpy array 找出符合条件的数并赋值
  7. MySQL的Limit子句
  8. 氩焊机器人编程_焊接机器人编程及调试方法
  9. (Object detection)目标检测从入门到精通——第五部分YOLO 算法
  10. tsd3dmapper软件使用方法_mybatis-plus的使用 ------ 入门
  11. 三种方法生成随机数之GetTickCount篇
  12. QT5/C++项目:基于QT的跨平台网络对战象棋(二)(推荐★★★★)
  13. OVER(PARTITION BY)函数介绍
  14. docker swarm英文文档学习-7-在集群中管理节点
  15. 软考 软件设计师 第五版+历年真题
  16. Datawhale数据分析学习——学术前沿趋势分析 任务1
  17. Python实现电子钢琴及封装
  18. 基于Eclipse和Mysql写的公交管理系统
  19. 安防监控流媒体服务器对接宇视摄像机配置OCX插件安装时出现Failed to register ocx, error code 14001错误问题分析
  20. 国务院新规发布:首次明确电击治网瘾犯法

热门文章

  1. java在Intellij上使用sigar
  2. 我的世界java种子 要塞,我的世界:稀奇种子,恐龙骨架出现在要塞,你绝对没见过...
  3. 独立游戏开发日志:2021年2月12日 改进版反弹跳
  4. 虹软科技Java人脸识别_java人脸识别 虹软ArcFace 2.0,java SDK使用、人脸识别-抽取人脸特征并做比对...
  5. 2021-01-29 大数据课程笔记 day9
  6. 数据结构教材答案(C语言版 严蔚敏)第一章 绪论
  7. python线程池和多线程
  8. USB转TTL、USB转232的区别
  9. java三级分销_java版微信三级分销完整源码
  10. Java视频教程从入门到精通(2021完整版)