课程概述

下方课件区域方向键控制翻页,f 键全屏。

小乌龟实例

该实例演示利用 Python 自带的 turtle 模块画图。

其中心型部分参考了 https://www.youtube.com/watch?v=2STVF2vCx2c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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()

网络爬虫

批量获取喜欢的图片。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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

Python 3.9 dlib whl 下载

Python 3.10 dlib whl 下载

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
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')