00-课程概述
下方课件区域方向键控制翻页,f 键全屏。
小乌龟实例¶
该实例演示利用 Python 自带的 turtle 模块画图。
其中心型部分参考了 https://www.youtube.com/watch?v=2STVF2vCx2c
import turtle
# 画一个弧度
def curve(t):
for i in range(200):
t.right(1)
t.forward(1)
# 创建一个空白的窗口
w = turtle.Screen()
# 创建一只小海龟
t = turtle.Turtle()
# 不画线,跑到 x=0 y=128 的位置
t.penup()
t.goto(0, 128)
# 落笔,笔的颜色是蓝色,写 anjingcuc
t.pendown()
t.pencolor('blue')
t.write('anjingcuc',
False,
align='center',
font=('Times New Roman', 64, 'normal'))
# 抬起笔,跑到 x=0 y=-50 的位置
t.penup()
t.goto(0, -50)
# 落笔,且线条颜色为红色,填充颜色为红色
t.pendown()
t.color('red', 'red')
# 画心型并填充
t.begin_fill()
t.left(140)
t.forward(111.65)
curve(t)
t.left(120)
curve(t)
t.forward(111.65)
t.end_fill()
# 抬笔,并跑到 x=0 y=-148 的位置
t.penup()
t.goto(0, -148)
# 落笔,颜色为绿色
t.pendown()
t.pencolor('green')
# 写 bilibili
t.write('bilibili',
False,
align='center',
font=('Times New Roman', 64, 'normal'))
# 隐藏箭头(即小乌龟)
t.hideturtle()
# 显示窗口
w.mainloop()
网络爬虫¶
批量获取喜欢的图片。
from pathlib import Path
import time
import requests
from bs4 import BeautifulSoup
# 添加 HTTP header 通过修改User-Agent字段伪装成Chrome
headers = {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
}
r = requests.get("https://www.zhihu.com/question/33797069/answer/961097566",
headers=headers)
# 用 BeautifulSoup 解析返回的 HTML
soup = BeautifulSoup(r.text, 'html.parser')
images = soup.select('div.RichContent-inner > span > figure > img')
image_number = 1
for image in images:
image_url = image['data-actualsrc']
image_path = str(image_number) + Path(image_url).suffix
# 下载文件
r = requests.get(image_url)
f = open(image_path, 'wb')
for chunk in r.iter_content(chunk_size=512 * 1024):
if chunk: # filter out keep-alive new chunks
f.write(chunk)
f.close()
# 有礼貌地间隔5秒获取一个文件
time.sleep(5)
image_number += 1
人工智能-换脸¶
一键换装?参考 萌新如何用 Python 实现人脸替换? / matthewearl/faceswap 。
import cv2
import dlib
import numpy
import sys, os
PREDICTOR_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)),
"shape_predictor_68_face_landmarks.dat")
SCALE_FACTOR = 1
FEATHER_AMOUNT = 11
# 1-16 脸型
JAW_POINTS = list(range(0, 17))
# 17-21 右边眉毛
RIGHT_BROW_POINTS = list(range(17, 22))
# 22-26 左边眉毛
LEFT_BROW_POINTS = list(range(22, 27))
# 27-35 鼻子
NOSE_POINTS = list(range(27, 35))
# 36-41 眼睛
RIGHT_EYE_POINTS = list(range(36, 42))
# 42-47 眼睛
LEFT_EYE_POINTS = list(range(42, 48))
# 48-68 嘴巴
MOUTH_POINTS = list(range(48, 68))
# 17-68 整个脸
FACE_POINTS = list(range(17, 68))
FACE_END = 68
# Points used to line up the images.
ALIGN_POINTS = (LEFT_BROW_POINTS + RIGHT_EYE_POINTS + LEFT_EYE_POINTS +
RIGHT_BROW_POINTS + NOSE_POINTS + MOUTH_POINTS)
# Points from the second image to overlay on the first. The convex hull of each
# element will be overlaid.
OVERLAY_POINTS = [FACE_POINTS]
# Amount of blur to use during colour correction, as a fraction of the
# pupillary distance.
COLOUR_CORRECT_BLUR_FRAC = 0.6
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(PREDICTOR_PATH)
class TooManyFaces(Exception):
pass
class NoFaces(Exception):
pass
def draw_point(img, p, color):
cv2.circle(img, (p[0], p[1]), 2, color, cv2.FILLED, cv2.LINE_AA, 0)
def rect_contains(rect, point):
if point[0] < rect[0]:
return False
elif point[1] < rect[1]:
return False
elif point[0] > rect[0] + rect[2]:
return False
elif point[1] > rect[1] + rect[3]:
return False
return True
def measure_triangle(shape, points):
rect = (0, 0, shape[1], shape[0])
sub_div = cv2.Subdiv2D(rect)
for p in points:
sub_div.insert((p[0], p[1]))
#sub_div.insert(points)
triangle_list = sub_div.getTriangleList()
triangle = []
for t in triangle_list:
pt = [(t[0], t[1]), (t[2], t[3]), (t[4], t[5])]
pt1 = (t[0], t[1])
pt2 = (t[2], t[3])
pt3 = (t[4], t[5])
if rect_contains(rect, pt1) and rect_contains(
rect, pt2) and rect_contains(rect, pt3):
ind = []
for j in range(0, 3):
for k in range(0, len(points)):
if abs(pt[j][0] - points[k][0]) < 1.0 and abs(
pt[j][1] - points[k][1]) < 1.0:
ind.append(k)
if len(ind) == 3:
triangle.append((ind[0], ind[1], ind[2]))
return triangle
def morph_triangle(src, dst, img, t_src, t_dst, t, alpha):
r1 = cv2.boundingRect(np.float32([t_src]))
r2 = cv2.boundingRect(np.float32([t_dst]))
r = cv2.boundingRect(np.float32([t]))
t1_rect = []
t2_rect = []
t_rect = []
for i in range(0, 3):
t_rect.append(((t[i][0] - r[0]), (t[i][1] - r[1])))
t1_rect.append(((t_src[i][0] - r1[0]), (t_src[i][1] - r1[1])))
t2_rect.append(((t_dst[i][0] - r2[0]), (t_dst[i][1] - r2[1])))
mask = np.zeros((r[3], r[2], 3), dtype=np.float32)
cv2.fillConvexPoly(mask, np.int32(t_rect), (1.0, 1.0, 1.0), 16, 0)
img1_rect = src[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]
img2_rect = dst[r2[1]:r2[1] + r2[3], r2[0]:r2[0] + r2[2]]
size = (r[2], r[3])
warp_img1 = affine_transform(img1_rect, t1_rect, t_rect, size)
warp_img2 = affine_transform(img2_rect, t2_rect, t_rect, size)
img_rect = (1.0 - alpha) * warp_img1 + alpha * warp_img2
img[r[1]:r[1] + r[3],
r[0]:r[0] + r[2]] = img[r[1]:r[1] + r[3], r[0]:r[0] +
r[2]] * (1 - mask) + img_rect * mask
def affine_triangle(src, dst, t_src, t_dst):
r1 = cv2.boundingRect(np.float32([t_src]))
r2 = cv2.boundingRect(np.float32([t_dst]))
t1_rect = []
t2_rect = []
t2_rect_int = []
for i in range(0, 3):
t1_rect.append((t_src[i][0] - r1[0], t_src[i][1] - r1[1]))
t2_rect.append((t_dst[i][0] - r2[0], t_dst[i][1] - r2[1]))
t2_rect_int.append((t_dst[i][0] - r2[0], t_dst[i][1] - r2[1]))
mask = np.zeros((r2[3], r2[2], 3), dtype=np.float32)
cv2.fillConvexPoly(mask, np.int32(t2_rect_int), (1.0, 1.0, 1.0), 16, 0)
img1_rect = src[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]
size = (r2[2], r2[3])
img2_rect = affine_transform(img1_rect, t1_rect, t2_rect, size)
img2_rect = img2_rect * mask
dst[r2[1]:r2[1] + r2[3], r2[0]:r2[0] +
r2[2]] = dst[r2[1]:r2[1] + r2[3], r2[0]:r2[0] + r2[2]] * (
(1.0, 1.0, 1.0) - mask)
dst[r2[1]:r2[1] + r2[3], r2[0]:r2[0] +
r2[2]] = dst[r2[1]:r2[1] + r2[3], r2[0]:r2[0] + r2[2]] + img2_rect
def affine_transform(src, src_tri, dst_tri, size):
warp_mat = cv2.getAffineTransform(np.float32(src_tri), np.float32(dst_tri))
dst = cv2.warpAffine(src,
warp_mat, (size[0], size[1]),
None,
flags=cv2.INTER_LINEAR,
borderMode=cv2.BORDER_REFLECT_101)
return dst
def get_landmarks(im):
rects = detector(im, 1)
if len(rects) > 1:
raise TooManyFaces
if len(rects) == 0:
raise NoFaces
# 特征提取器(predictor)要一个粗糙的边界框作为算法输入,由传统的能返回一个矩形列表的人脸检测器(detector)提供,其每个矩形列表在图像中对应一个脸。
return numpy.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()])
def draw_convex_hull(im, points, color):
points = cv2.convexHull(points)
cv2.fillConvexPoly(im, points, color=color)
# 用普氏分析法(Procrustes Analysis)实现人脸对齐
def transformation_from_points(points1, points2):
"""
Return an affine transformation [s * R | T] such that:
sum ||s*R*p1,i + T - p2,i||^2
is minimized.
"""
# Solve the procrustes problem by subtracting centroids, scaling by the
# standard deviation, and then using the SVD to calculate the rotation. See
# the following for more details:
# https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem
# 1.将2个图片的脸部浮点转换成矩阵
points1 = points1.astype(numpy.float64)
points2 = points2.astype(numpy.float64)
# 2.将每一个点集减去它的矩心。一旦为这两个新的点集找到了一个最佳的缩放和旋转方法,这两个矩心c1和c2就可以用来找到完整的解决方案。
c1 = numpy.mean(points1, axis=0)
c2 = numpy.mean(points2, axis=0)
points1 -= c1
points2 -= c2
# 3.同样,将每一个点集除以它的标准偏差。这消除了缩放偏差。
s1 = numpy.std(points1)
s2 = numpy.std(points2)
points1 /= s1
points2 /= s2
# 4.使用奇异值分解(singular value decomposition)计算旋转部分。请参阅维基百科有关Orthogonal Procrustes Problem的文章,以了解它的具体工作原理。
U, S, Vt = numpy.linalg.svd(points1.T * points2)
# The R we seek is in fact the transpose of the one given by U * Vt. This
# is because the above formulation assumes the matrix goes on the right
# (with row vectors) where as our solution requires the matrix to be on the
# left (with column vectors).
R = (U * Vt).T
# 5.将整个变换过程以仿射变换矩阵形式返回。
return numpy.vstack([
numpy.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T)),
numpy.matrix([0., 0., 1.])
])
def read_im_and_landmarks(fname):
im = cv2.imread(fname, cv2.IMREAD_COLOR)
im = cv2.resize(im,
(im.shape[1] * SCALE_FACTOR, im.shape[0] * SCALE_FACTOR))
s = get_landmarks(im)
return im, s
def warp_im(im, M, dshape):
output_im = numpy.zeros(dshape, dtype=im.dtype)
cv2.warpAffine(im,
M[:2], (dshape[1], dshape[0]),
dst=output_im,
borderMode=cv2.BORDER_TRANSPARENT,
flags=cv2.WARP_INVERSE_MAP)
return output_im
def get_face_mask(im, landmarks):
im = numpy.zeros(im.shape[:2], dtype=numpy.float64)
for group in OVERLAY_POINTS:
draw_convex_hull(im, landmarks[group], color=1)
im = numpy.array([im, im, im]).transpose((1, 2, 0))
im = (cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) > 0) * 1.0
im = cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0)
return im
def correct_colours(im1, im2, landmarks1):
blur_amount = COLOUR_CORRECT_BLUR_FRAC * numpy.linalg.norm(
numpy.mean(landmarks1[LEFT_EYE_POINTS], axis=0) -
numpy.mean(landmarks1[RIGHT_EYE_POINTS], axis=0))
blur_amount = int(blur_amount)
if blur_amount % 2 == 0:
blur_amount += 1
im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0)
im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0)
# Avoid divide-by-zero errors.
im2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype)
return (im2.astype(numpy.float64) * im1_blur.astype(numpy.float64) /
im2_blur.astype(numpy.float64))
def change_face(src_img, dst_img, out_img):
# 一、将face变形成model的形状
# 1.将两张图片转化成numpy数组,并返回一个68 x2元素矩阵,输入图像的每个特征点对应每行的一个x,y坐标。
model_img, model_landmarks = read_im_and_landmarks(src_img)
face_img, face_landmarks = read_im_and_landmarks(dst_img)
# 2.用普氏分析法(Procrustes Analysis)实现人脸对齐
M = transformation_from_points(model_landmarks[ALIGN_POINTS],
face_landmarks[ALIGN_POINTS])
mask = get_face_mask(face_img, face_landmarks)
warped_mask = warp_im(mask, M, model_img.shape)
combined_mask = numpy.max(
[get_face_mask(model_img, model_landmarks), warped_mask], axis=0)
warped_im2 = warp_im(face_img, M, model_img.shape)
warped_corrected_im2 = correct_colours(model_img, warped_im2,
model_landmarks)
output_im = model_img * (
1.0 - combined_mask) + warped_corrected_im2 * combined_mask
cv2.imwrite(out_img, output_im)
if __name__ == '__main__':
change_face('target.jpg', 'face.jpg', 'output.jpg')