Kears+Opencv实现简单人脸识别


Posted in Python onAugust 28, 2019

写在前面:这篇文章也是借鉴了一些前辈的代码和思路写的,代码有些也是复用了别人的。

先说下思路:

1.首先利用Opencv检测出人脸的区域 

2.在成功的检测出人脸区域后,将识别出的人脸区域截取成图片,储存起来,用作后续的训练数据。

3.在获取到了足够多的数据后,搭建CNN网络进行训练。

5.训练完成后,将模型存储起来。 6.在利用Opencv实时读取视频流,将检测出人脸的区域变成图片放入模型中进行预测。

以上就是这个项目的一个基本的思路了。

1.利用Opencv检测人脸的代码如下,这个代码在Opencv的官方文档中也有,最重要的是加载的xml文件,因为这个xml文件中是保存这已经训练好的人脸检测模型。

import cv2
 
def identify_face(window_name, camera_idx):
  cv2.namedWindow(window_name)
 
  # 视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头
  cap = cv2.VideoCapture(camera_idx)
 
  # 告诉OpenCV使用人脸识别分类器
  classfier = cv2.CascadeClassifier("haarcascade_frontalface_alt2.xml")
 
  # 识别出人脸后要画的边框的颜色,RGB格式
  color = (0, 255, 0)
 
  while cap.isOpened(): # 是否初始化摄像头设备
    ok, frame = cap.read() # 读取一帧数据
    if not ok:
      break
 
    # 将当前帧转换成灰度图像
    grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
 
 
    # 人脸检测,1.2和4分别为图片缩放比例和需要检测的有效点数
    faceRects = classfier.detectMultiScale(grey, scaleFactor=1.2, minNeighbors=4, minSize=(32, 32))
    if len(faceRects) > 0: # 大于0则检测到人脸
      for faceRect in faceRects: # 单独框出每一张人脸
        x, y, w, h = faceRect  # 获得人脸的左上角对应坐标,及宽度和高度
        cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
 
    # 显示图像
    cv2.imshow(window_name, frame)
    c = cv2.waitKey(10)
    if c & 0xFF == ord('q'):
      break
 
  # 释放摄像头并销毁所有窗口
  cap.release()
  cv2.destroyAllWindows()
 
 
if __name__ == '__main__':
  identify_face("identify face", 0)

2.在检测出人脸区域后,将人脸区域截取成图片保存下来

import cv2
from threading import Thread
 
 
 
def identify_face_and_store_face_image(window_name, camera_idx):
  cv2.namedWindow(window_name)
 
  # 视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头
  cap = cv2.VideoCapture(camera_idx)
 
  # 告诉OpenCV使用人脸识别分类器
  classfier = cv2.CascadeClassifier("haarcascade_frontalface_alt2.xml")
 
  # 识别出人脸后要画的边框的颜色,RGB格式
  color = (0, 255, 0)
 
 
  # 保存图片的索引
  num = 0
  while cap.isOpened(): # 是否初始化摄像头设备
    ok, frame = cap.read() # 读取一帧数据
    if not ok:
      break
 
    # 将当前帧转换成灰度图像
    grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
 
 
    # 人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
    faceRects = classfier.detectMultiScale(grey, scaleFactor=1.2, minNeighbors=4, minSize=(32, 32))
    if len(faceRects) > 0: # 大于0则检测到人脸
      for faceRect in faceRects: # 单独框出每一张人脸
        x, y, w, h = faceRect  # 获得人脸的左上角对应坐标,及宽度和高度
 
        cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
 
        # store_face_image(frame, h, num, w, x, y)
        # 开启一个线程去存储人脸图片
        t = Thread(target=store_face_image, args=(frame, h, num, w, x, y, ))
        t.start()
 
 
        # 显示捕捉了多少张人脸照片
        font = cv2.FONT_HERSHEY_SIMPLEX # 字体
        cv2.putText(frame, ('num %d' % num), (x + 30, y + 30), font, 1, (255, 0, 255), 2)
 
        num += 1
        if num <= 1000: # 保存1000张图片后就退出
          break
 
 
    if num >= 1000:
      break
 
    # 显示图像
    cv2.imshow(window_name, frame)
    c = cv2.waitKey(10)
    if c & 0xFF == ord('q'):
      break
 
  # 释放摄像头并销毁所有窗口
  cap.release()
  cv2.destroyAllWindows()
 
 
def store_face_image(frame, h, num, w, x, y):
  # 将当前帧保存为图片
  img_name = '%s/%d.jpg' % (r'face_image', num)
  image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
  cv2.imwrite(img_name, image)
 
if __name__ == '__main__':
  identify_face_and_store_face_image("identify face", 0)

3.将保存的数据进行处理,比如打上标签,进行归一化等等,下列代码是load_datasets.py文件中的

import os
import sys
import numpy as np
import cv2
 
IMAGE_SIZE = 64
 
 
# 按照指定图像大小调整尺寸
def resize_image(image, height=IMAGE_SIZE, width=IMAGE_SIZE):
  top, bottom, left, right = (0, 0, 0, 0)
 
  # 获取图像尺寸
  h, w, _ = image.shape
 
  # 对于长宽不相等的图片,找到最长的一边
  longest_edge = max(h, w)
 
  # 计算短边需要增加多上像素宽度使其与长边等长
  if h < longest_edge:
    dh = longest_edge - h
    top = dh // 2
    bottom = dh - top
  elif w < longest_edge:
    dw = longest_edge - w
    left = dw // 2
    right = dw - left
  else:
    pass
 
  # RGB颜色
  BLACK = [0, 0, 0]
 
  # 给图像增加边界,是图片长、宽等长,cv2.BORDER_CONSTANT指定边界颜色由value指定
  constant = cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT, value=BLACK)
 
  # 调整图像大小并返回
  return cv2.resize(constant, (height, width))
 
 
# 读取训练数据
images = []
labels = []
 
 
def read_path(path_name):
  for dir_item in os.listdir(path_name):
    # 从初始路径开始叠加,合并成可识别的操作路径
    full_path = os.path.abspath(os.path.join(path_name, dir_item))
 
    if os.path.isdir(full_path): # 如果是文件夹,继续递归调用
      read_path(full_path)
    else: # 文件
      if dir_item.endswith('.jpg'):
        image = cv2.imread(full_path)
        image = resize_image(image, IMAGE_SIZE, IMAGE_SIZE)
 
        # 放开这个代码,可以看到resize_image()函数的实际调用效果
        # cv2.imwrite('1.jpg', image)
 
        images.append(image)
        labels.append(path_name)
 
  return images, labels
 
 
# 从指定路径读取训练数据
def load_dataset(path_name):
  images, labels = read_path(path_name)
 
  # 将输入的所有图片转成四维数组,尺寸为(图片数量*IMAGE_SIZE*IMAGE_SIZE*3)
  # 共587张图片,IMAGE_SIZE为64,故对我来说尺寸为587 * 64 * 64 * 3
  # 图片为64 * 64像素,一个像素3个颜色值(RGB)
  images = np.array(images)
  print(images.shape)
 
  # 标注数据,'my_face_image'文件夹下都是我的脸部图像,全部指定为0,你可以在获取他人的脸部图片比如说同学的,指定为1,
  labels = np.array([0 if label.endswith('my_face_image') else 1 for label in labels])
 
  return images, labels
 
 
if __name__ == '__main__':
  if len(sys.argv) != 1:
    print("Usage:%s path_name\r\n" % (sys.argv[0]))
  else:
    images, labels = load_dataset("face_image")

4.搭建模型,进行训练

import random
 
import numpy as np
from sklearn.model_selection import train_test_split
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Convolution2D, MaxPooling2D
from keras.optimizers import SGD
from keras.utils import np_utils
from keras.models import load_model
from keras import backend as K
 
from load_datasets import load_dataset, resize_image, IMAGE_SIZE
 
 
class Dataset:
  def __init__(self, path_name):
    # 训练集
    self.train_images = None
    self.train_labels = None
 
    # 验证集
    self.valid_images = None
    self.valid_labels = None
 
    # 测试集
    self.test_images = None
    self.test_labels = None
 
    # 数据集加载路径
    self.path_name = path_name
 
    # 当前库采用的维度顺序
    self.input_shape = None
 
  # 加载数据集并按照交叉验证的原则划分数据集并进行相关预处理工作
  def load(self, img_rows=IMAGE_SIZE, img_cols=IMAGE_SIZE,
       img_channels=3, nb_classes=2):
    # 加载数据集到内存
    images, labels = load_dataset(self.path_name)
 
    train_images, valid_images, train_labels, valid_labels = train_test_split(images, labels, test_size=0.3,
                                         random_state=random.randint(0, 100))
    _, test_images, _, test_labels = train_test_split(images, labels, test_size=0.5,
                             random_state=random.randint(0, 100))
 
    # 当前的维度顺序如果为'th',则输入图片数据时的顺序为:channels,rows,cols,否则:rows,cols,channels
    # 这部分代码就是根据keras库要求的维度顺序重组训练数据集
    if K.image_dim_ordering() == 'th':
      train_images = train_images.reshape(train_images.shape[0], img_channels, img_rows, img_cols)
      valid_images = valid_images.reshape(valid_images.shape[0], img_channels, img_rows, img_cols)
      test_images = test_images.reshape(test_images.shape[0], img_channels, img_rows, img_cols)
      self.input_shape = (img_channels, img_rows, img_cols)
    else:
      train_images = train_images.reshape(train_images.shape[0], img_rows, img_cols, img_channels)
      valid_images = valid_images.reshape(valid_images.shape[0], img_rows, img_cols, img_channels)
      test_images = test_images.reshape(test_images.shape[0], img_rows, img_cols, img_channels)
      self.input_shape = (img_rows, img_cols, img_channels)
 
      # 输出训练集、验证集、测试集的数量
      print(train_images.shape[0], 'train samples')
      print(valid_images.shape[0], 'valid samples')
      print(test_images.shape[0], 'test samples')
 
      # 我们的模型使用categorical_crossentropy作为损失函数,因此需要根据类别数量nb_classes将
      # 类别标签进行one-hot编码使其向量化,在这里我们的类别只有两种,经过转化后标签数据变为二维
      train_labels = np_utils.to_categorical(train_labels, nb_classes)
      valid_labels = np_utils.to_categorical(valid_labels, nb_classes)
      test_labels = np_utils.to_categorical(test_labels, nb_classes)
 
      # 像素数据浮点化以便归一化
      train_images = train_images.astype('float32')
      valid_images = valid_images.astype('float32')
      test_images = test_images.astype('float32')
 
      # 将其归一化,图像的各像素值归一化到0~1区间
      train_images /= 255
      valid_images /= 255
      test_images /= 255
 
      self.train_images = train_images
      self.valid_images = valid_images
      self.test_images = test_images
      self.train_labels = train_labels
      self.valid_labels = valid_labels
      self.test_labels = test_labels
 
 
# CNN网络模型类
class Model:
  def __init__(self):
    self.model = None
 
    # 建立模型
 
  def build_model(self, dataset, nb_classes=2):
    # 构建一个空的网络模型,它是一个线性堆叠模型,各神经网络层会被顺序添加,专业名称为序贯模型或线性堆叠模型
    self.model = Sequential()
 
    # 以下代码将顺序添加CNN网络需要的各层,一个add就是一个网络层
    self.model.add(Convolution2D(32, 3, 3, border_mode='same',
                   input_shape=dataset.input_shape)) # 1 2维卷积层
    self.model.add(Activation('relu')) # 2 激活函数层
 
    self.model.add(Convolution2D(32, 3, 3)) # 3 2维卷积层
    self.model.add(Activation('relu')) # 4 激活函数层
 
    self.model.add(MaxPooling2D(pool_size=(2, 2))) # 5 池化层
    self.model.add(Dropout(0.25)) # 6 Dropout层
 
    self.model.add(Convolution2D(64, 3, 3, border_mode='same')) # 7 2维卷积层
    self.model.add(Activation('relu')) # 8 激活函数层
 
    self.model.add(Convolution2D(64, 3, 3)) # 9 2维卷积层
    self.model.add(Activation('relu')) # 10 激活函数层
 
    self.model.add(MaxPooling2D(pool_size=(2, 2))) # 11 池化层
    self.model.add(Dropout(0.25)) # 12 Dropout层
 
    self.model.add(Flatten()) # 13 Flatten层
    self.model.add(Dense(512)) # 14 Dense层,又被称作全连接层
    self.model.add(Activation('relu')) # 15 激活函数层
    self.model.add(Dropout(0.5)) # 16 Dropout层
    self.model.add(Dense(nb_classes)) # 17 Dense层
    self.model.add(Activation('softmax')) # 18 分类层,输出最终结果
 
    # 输出模型概况
    self.model.summary()
 
  # 训练模型
  def train(self, dataset, batch_size=20, nb_epoch=10, data_augmentation=True):
    sgd = SGD(lr=0.01, decay=1e-6,
         momentum=0.9, nesterov=True) # 采用SGD+momentum的优化器进行训练,首先生成一个优化器对象
    self.model.compile(loss='categorical_crossentropy',
              optimizer=sgd,
              metrics=['accuracy']) # 完成实际的模型配置工作
 
    # 不使用数据提升,所谓的提升就是从我们提供的训练数据中利用旋转、翻转、加噪声等方法创造新的
    # 训练数据,有意识的提升训练数据规模,增加模型训练量
    if not data_augmentation:
      self.model.fit(dataset.train_images,
              dataset.train_labels,
              batch_size=batch_size,
              nb_epoch=nb_epoch,
              validation_data=(dataset.valid_images, dataset.valid_labels),
              shuffle=True)
    # 使用实时数据提升
    else:
      # 定义数据生成器用于数据提升,其返回一个生成器对象datagen,datagen每被调用一
      # 次其生成一组数据(顺序生成),节省内存,其实就是python的数据生成器
      datagen = ImageDataGenerator(
        featurewise_center=False, # 是否使输入数据去中心化(均值为0),
        samplewise_center=False, # 是否使输入数据的每个样本均值为0
        featurewise_std_normalization=False, # 是否数据标准化(输入数据除以数据集的标准差)
        samplewise_std_normalization=False, # 是否将每个样本数据除以自身的标准差
        zca_whitening=False, # 是否对输入数据施以ZCA白化
        rotation_range=20, # 数据提升时图片随机转动的角度(范围为0~180)
        width_shift_range=0.2, # 数据提升时图片水平偏移的幅度(单位为图片宽度的占比,0~1之间的浮点数)
        height_shift_range=0.2, # 同上,只不过这里是垂直
        horizontal_flip=True, # 是否进行随机水平翻转
        vertical_flip=False) # 是否进行随机垂直翻转
 
      # 计算整个训练样本集的数量以用于特征值归一化、ZCA白化等处理
      datagen.fit(dataset.train_images)
 
      # 利用生成器开始训练模型
      self.model.fit_generator(datagen.flow(dataset.train_images, dataset.train_labels,
                         batch_size=batch_size),
                   samples_per_epoch=dataset.train_images.shape[0],
                   nb_epoch=nb_epoch,
                   validation_data=(dataset.valid_images, dataset.valid_labels))
 
  MODEL_PATH = 'zhuhaipeng.face.model.h5'
 
  def save_model(self, file_path=MODEL_PATH):
    self.model.save(file_path)
 
  def load_model(self, file_path=MODEL_PATH):
    self.model = load_model(file_path)
 
  def evaluate(self, dataset):
    score = self.model.evaluate(dataset.test_images, dataset.test_labels, verbose=1)
    print("%s: %.2f%%" % (self.model.metrics_names[1], score[1] * 100))
 
  # 识别人脸
  def face_predict(self, image):
    # 依然是根据后端系统确定维度顺序
    if K.image_dim_ordering() == 'th' and image.shape != (1, 3, IMAGE_SIZE, IMAGE_SIZE):
      image = resize_image(image) # 尺寸必须与训练集一致都应该是IMAGE_SIZE x IMAGE_SIZE
      image = image.reshape((1, 3, IMAGE_SIZE, IMAGE_SIZE)) # 与模型训练不同,这次只是针对1张图片进行预测
    elif K.image_dim_ordering() == 'tf' and image.shape != (1, IMAGE_SIZE, IMAGE_SIZE, 3):
      image = resize_image(image)
      image = image.reshape((1, IMAGE_SIZE, IMAGE_SIZE, 3))
 
    # 浮点并归一化
    image = image.astype('float32')
    image /= 255
 
    # 给出输入属于各个类别的概率,我们是二值类别,则该函数会给出输入图像属于0和1的概率各为多少
    result = self.model.predict_proba(image)
    print('result:', result)
 
    # 给出类别预测:0或者1
    result = self.model.predict_classes(image)
 
    # 返回类别预测结果
    return result[0]
 
 
if __name__ == '__main__': # 训练模型
  dataset = Dataset('face_image')
  dataset.load()
 
  model = Model()
  model.build_model(dataset)
 
  # 测试训练函数的代码
  model.train(dataset)
 
if __name__ == '__main__': # 训练并保存模型
  dataset = Dataset('face_image')
  dataset.load()
 
  model = Model()
  model.build_model(dataset)
  model.train(dataset)
  model.save_model(file_path='model/zhuhaipeng.face.model.h5')
 
if __name__ == '__main__':  # 读取模型进行评估
  dataset = Dataset('face_image')
  dataset.load()
 
  # 评估模型
  model = Model()
  model.load_model(file_path='model/zhuhaipeng.face.model.h5')
  model.evaluate(dataset)

5.训练好模型后,使用Opencv实时读取视频流检测人脸位置,再将人脸放入模型中进行预测

# -*- coding: utf-8 -*-
 
import cv2
import sys
from face_train import Model
 
if __name__ == '__main__':
  if len(sys.argv) != 1:
    print("Usage:%s camera_id\r\n" % (sys.argv[0]))
    sys.exit(0)
 
  # 加载模型
  model = Model()
  model.load_model(file_path='model/zhuhaipeng.face.model.h5')
 
  # 框住人脸的矩形边框颜色
  color = (0, 255, 0)
 
  # 捕获指定摄像头的实时视频流
  cap = cv2.VideoCapture(0)
 
  # 人脸识别分类器本地存储路径
  cascade_path = "haarcascade_frontalface_alt2.xml"
 
  # 循环检测识别人脸
  while True:
    ret, frame = cap.read() # 读取一帧视频
 
    if ret is True:
 
      # 图像灰化,降低计算复杂度
      frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    else:
      continue
    # 使用人脸识别分类器,读入分类器
    cascade = cv2.CascadeClassifier(cascade_path)
 
    # 利用分类器识别出哪个区域为人脸
    faceRects = cascade.detectMultiScale(frame_gray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
    if len(faceRects) > 0:
      for faceRect in faceRects:
        x, y, w, h = faceRect
 
        # 截取脸部图像提交给模型识别这是谁
        image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
        faceID = model.face_predict(image)
 
        # 如果是“我”
        if faceID == 0:
          cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, thickness=2)
 
          # 文字提示是谁
          cv2.putText(frame, 'zhuhaipeng',
                (x + 30, y + 30), # 坐标
                cv2.FONT_HERSHEY_SIMPLEX, # 字体
                1, # 字号
                (255, 0, 255), # 颜色
                2) # 字的线宽
        else: # 如果不是我
          # 文字提示未知
          cv2.putText(frame, 'Unknown people ',
                (x + 30, y + 30), # 坐标
                cv2.FONT_HERSHEY_SIMPLEX, # 字体
                1, # 字号
                (255, 0, 255), # 颜色
                2) # 字的线宽
 
    cv2.imshow("identify me", frame)
 
    # 等待10毫秒看是否有按键输入
    k = cv2.waitKey(10)
    # 如果输入q则退出循环
    if k & 0xFF == ord('q'):
      break
 
  # 释放摄像头并销毁所有窗口
  cap.release()
  cv2.destroyAllWindows()

到此一个简单的人脸识别小项目就完成了,在这个项目中,只是简单的二分类,可以在此基础上进行扩展为多分类,如果识别的准确率低的话,可以尝试更改网络架构,或者对数据进行预处理等等,如果大家有兴趣改进的话,可以尝试一下。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
使用Python对Access读写操作
Mar 30 Python
python引入导入自定义模块和外部文件的实例
Jul 24 Python
python3 实现对图片进行局部切割的方法
Dec 05 Python
解决Pandas的DataFrame输出截断和省略的问题
Feb 08 Python
Python常用特殊方法实例总结
Mar 22 Python
基于Python3.6中的OpenCV实现图片色彩空间的转换
Feb 03 Python
Python GUI编程学习笔记之tkinter中messagebox、filedialog控件用法详解
Mar 30 Python
基于keras中的回调函数用法说明
Jun 17 Python
Python数据可视化实现漏斗图过程图解
Jul 20 Python
Python 如何展开嵌套的序列
Aug 01 Python
pandas:get_dummies()与pd.factorize()的用法及区别说明
May 21 Python
一篇文章带你了解Python和Java的正则表达式对比
Sep 15 Python
解决python 文本过滤和清理问题
Aug 28 #Python
解决Python对齐文本字符串问题
Aug 28 #Python
python+jinja2实现接口数据批量生成工具
Aug 28 #Python
解决Atom安装Hydrogen无法运行python3的问题
Aug 28 #Python
opencv实现简单人脸识别
Feb 19 #Python
Atom Python 配置Python3 解释器的方法
Aug 28 #Python
python聚类算法解决方案(rest接口/mpp数据库/json数据/下载图片及数据)
Aug 28 #Python
You might like
全国FM电台频率大全 - 31 新疆维吾尔族自治区
2020/03/11 无线电
PHP XML数据解析代码
2010/05/26 PHP
php采集自中央气象台范围覆盖全国的天气预报代码实例
2015/01/04 PHP
PHP版本常用的排序算法汇总
2015/12/20 PHP
TP3.2批量上传文件或图片 同名冲突问题的解决方法
2017/08/01 PHP
PHP实现负载均衡session共享redis缓存操作示例
2018/08/22 PHP
在JavaScript中遭遇级联表达式陷阱
2007/03/08 Javascript
jQuery MD5加密实现代码
2010/03/15 Javascript
js操作textarea方法集合封装(兼容IE,firefox)
2011/02/22 Javascript
关于window.pageYOffset和document.documentElement.scrollTop
2011/04/05 Javascript
在javascript中对于DOM的加强
2013/04/11 Javascript
jquery的map与get方法详解
2013/11/04 Javascript
用js判断输入是否为中文的函数
2014/03/10 Javascript
浅谈JavaScript 执行环境、作用域及垃圾回收
2016/05/31 Javascript
DIV+CSS+jQ实现省市联动可扩展
2016/06/22 Javascript
javascript 广告移动特效的实现代码
2016/06/25 Javascript
jqGrid用法汇总(全经典)
2016/06/28 Javascript
XMLHttpRequest Level 2 使用指南
2016/08/26 Javascript
使用JavaScript为一张图片设置备选路径的方法
2017/01/04 Javascript
基于JS实现移动端向左滑动出现删除按钮功能
2017/02/22 Javascript
Javascript之图片的延迟加载的实例详解
2017/07/24 Javascript
node跨域转发 express+http-proxy-middleware的使用
2018/05/31 Javascript
JS实现集合的交集、补集、差集、去重运算示例【ES5与ES6写法】
2019/02/18 Javascript
在HTML中使用JavaScript的两种方法
2020/12/24 Javascript
[01:07:19]2018DOTA2亚洲邀请赛 4.5 淘汰赛 Mineski vs VG 第一场
2018/04/06 DOTA
[59:35]DOTA2-DPC中国联赛定级赛 Aster vs DLG BO3第一场 1月8日
2021/03/11 DOTA
Python中利用sorted()函数排序的简单教程
2015/04/27 Python
Python虚拟环境项目实例
2017/11/20 Python
在tensorflow中设置使用某一块GPU、多GPU、CPU的操作
2020/02/07 Python
Python代码执行时间测量模块timeit用法解析
2020/07/01 Python
美国领先的在线邮轮旅游公司:CruiseDirect
2018/06/07 全球购物
年会搞笑主持词
2014/03/27 职场文书
家庭教育的心得体会
2014/09/01 职场文书
合理化建议书
2015/02/04 职场文书
实现GO语言对数组切片去重
2022/04/20 Golang
Mysql 一主多从的部署
2022/05/20 MySQL