前言

本人在编写qqbot的时候已经自学过一些python,但是理解的不是很深刻,于是选了一门不怎么水的限选课叫“面向大数据的Python开发”。很不幸,这门课有实验报告要写,这个合集会用来收集所有的实验报告,希望对你有用。

需要补充的是,我用的编译器是pycharm,毕竟淘宝买了jetbrains的全家桶,不用会显得很亏。

实验一

使用def函数输出“hello world”

典型的脱裤子放屁需求,但是还是要写

1
2
3
4
5
def print_hi(name):
print(f'{name}')

if __name__ == '__main__':
print_hi('hello world!')

使用print的“sep”关键字参数打印出井字棋板

这里就用到了print方法,咱们来看一下语法和对应的参数

1
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
  • objects – 复数,表示可以一次输出多个对象。输出多个对象时,需要用 , 分隔。
  • sep – 用来间隔多个对象,默认值是一个空格。
  • end – 用来设定以什么结尾。默认值是换行符 \n,我们可以换成其他字符串。
  • file – 要写入的文件对象。
  • flush – 输出是否被缓存通常决定于 file,但如果 flush 关键字参数为 True,流会被强制刷新。

下面咱们就来实现这个需求

1
2
3
4
5
6
7
8
9
10
11
def print_style1():  #第一种横线格式
print('--', '--', '--', sep='+')

def print_style2(): #第二种横线格式
print(' ', ' ', ' ', sep='|')

if __name__ == '__main__':
for i in range(2):
print_style1()
print_style2()
print_style1()

打印一个超级井字棋盘

既然已经实现了打印小方格,打印大棋盘无非就是按照需求增加方法和循环次数罢了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def print_row():
print(' | ', '', ' |', sep=' | H | ')

def print_col():
print('--+-', '', '-+--', sep='-+--H--+-')

def print_par():
print('====', '', '====', sep='====+====')

if __name__ == '__main__':
num = 0
while num < 2:
count = 0
while count < 2:
print_row()
print_col()
if count == 1: print_row()
count = count + 1
if num < 1: print_par()
num = num+1

这个实验一有很多种实现的方法,我可能选择了最蠢的那一种

实验二

这节实验课主要学习的是函数,首先,先来看看如何定义函数:

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 **( )**。
  • 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
  • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  • 函数内容以冒号 : 起始,并且缩进。
  • return [表达式] 结束函数,选择性地返回一个值给调用方,不带表达式的 return 相当于返回 None。
1
2
def 函数名(参数列表):
函数体

编写函数输出字符串

实际上,这个函数是用来控制输出语句的句末标点和大小写,下面就让我们来试试吧

1
2
3
4
5
def print_out(message, point, num, cap):  #这四个参数分别对应输入字符串,标点类型,标点个数,大小写
message += point * num #将标点加入句末
if cap: #这里的cap是布尔类型,True返回小写,False返回大写
return message
return message.upper()

调用方法的时候记得按顺序传入参数(应该不会有人不知道吧)

使用函数计算平均值

平均数都会求吧,总和除以个数,python贴心的为你提供了求和和求个数的方法

1
2
3
4
def average(*nums):
if not nums:
return None
return sum(nums)/len(nums)

实验三

图像压缩算法

1
2
3
4
import numpy as np
from scipy.ndimage import convolve
import matplotlib.pyplot as plt
import sysa
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
# 这个板块是干什么用的呢?简单来说就是分析输入图像的基本信息
# 你需要输入一个图像,在程序中会先转化为一个大小为 NxMx3 的 3d numpy 数组,其中第 3 维用于颜色通道。
# 然后,就会输出包含能量的大小为 NxM 的数组
# 可以看到,底下我搞了很多函数来处理相关的计算,当然我相信你也没时间看了,就简要的写了写输入输出是啥

# 计算图像的“能量”,使用每个颜色通道的拉普拉斯算子并将它们相加。
# 输入:图像,一个大小为 NxMx3 的 3d numpy 数组
# 输出:能量图像,一个大小为 NxM 的二维 numpy 数组
def energy(image):
dy = np.array([-1, 0, 1])[:,None,None]
dx = np.array([-1, 0, 1])[None,:,None]
energy_img = convolve(image, dx)**2 + convolve(image, dy)**2
return np.sum(energy_img, axis=2)

# 找到图像中的最小能量水平接缝。
# 输入:一个包含能量值的二维 numpy 数组。,尺码 NxM。
# 输出:接缝表示为长度为 M 的一维数组,所有值都在 0 到 N-1 之间。
def find_horizontal_seam(energy):
return find_vertical_seam(energy.T)

# 从图像中删除垂直接缝:
# 输入:图像,一个包含像素值的二维 numpy 数组。 尺码 NxM。
# 接缝,一维数组(或列表)包含接缝中每个像素的列索引。 长度 N,所有值介于 0 和 M-1 之间。
# 输出:一个缩小了 1 列的新图像。 尺寸 N × M-1。
def remove_vertical_seam(image, seam):
height = image.shape[0]
linear_inds = np.array(seam)+np.arange(image.shape[0])*image.shape[1]
new_image = np.zeros((height,image.shape[1]-1,3), dtype="uint8")
for c in range(3):
temp = np.delete(image[:,:,c], linear_inds.astype(int))
temp = np.reshape(temp, (height, image.shape[1]-1))
new_image[:,:,c] = temp
return new_image

# 与上面的 remove_vertical_seam 相同,但用于水平接缝。 输出图像大小为 N-1 x M。
def remove_horizontal_seam(image, seam):
return np.transpose(remove_vertical_seam(np.transpose(image,(1,0,2)), seam), (1,0,2))

# 将 NxM 图像调整为所需的高度和宽度。
# 注意:此功能只会使图像变小。 放大图像未实现。
# 输入:图像,一个大小为 NxMx3 的 3d numpy 数组
# desired_width,一个给出所需宽度的整数
# desired_height,一个给出所需高度的整数
# 输出:调整后的图像,现在大小为 N x desired_width x 3
def seam_carve(image, desired_height, desired_width):
while image.shape[1] > desired_width:
seam = find_vertical_seam(energy(image))
assert len(seam) == image.shape[0], "the length of the seam must equal the height of the image"
image = remove_vertical_seam(image, seam)
sys.stdout.write('\rWidth is now %d' % image.shape[1])
print()
while image.shape[0] > desired_height:
seam = find_horizontal_seam(energy(image))
assert len(seam) == image.shape[1], "the length of the seam must equal the width of the image"
image = remove_horizontal_seam(image, seam)
sys.stdout.write('\rHeight is now %d' % image.shape[0])
print()
return image

# 说来惭愧,前几天写的代码我今天已经有点看不懂了,回忆许久,大概是这样

开始递归

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
def find_vertical_seam(energy):
costs = dict()
for i in range(energy.shape[1]):
best_seam, best_cost = fvs(energy, [i], 0.0)
costs[tuple(best_seam)] = best_cost
return min(costs, key=costs.get)# M 个最佳接缝中的最佳接缝

def fvs(energy, seam, cost):
row = len(seam)-1
col = seam[-1]

# 如果两侧之一越界,返回无穷大,常见的递归终止(笑),我期末考试就是这道题卡了好久
if col < 0 or col >= energy.shape[1]:
return seam, np.inf

cost += energy[row,col]

# 如果算法顺利的话,最常规基本情况就是平安到达图像底部
if len(seam) == energy.shape[0]:
return seam, cost

return min((fvs(energy, seam+[col], cost),
fvs(energy, seam+[col+1],cost),
fvs(energy, seam+[col-1],cost)),key=lambda x:x[1])
# 开始递归了,显然,我懒得想一个优化算法了,这里你可以加入一个优化矩阵算法提高速度,但我觉得没必要
# 通过定义接缝的能量函数并每次重新计算接缝的能量

验证压缩算法

1
2
3
4
5
6
7
8
9
10
11
12
13
# 接下来我们先来验证一下这个算法的正确性吧
# 通过压缩随机的颜色图像
np.random.seed(1)
h = 13
w = 13
img = np.random.randint(0,255,(h,w,3)).astype("uint8")
print(img.shape)
plt.figure()
plt.imshow(img)
e = energy(img)
img2 = seam_carve(img, h, w-1)
plt.figure()
plt.imshow(img2)

线性规划实现

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
import pulp
# PuLP是一个开源的第三方工具包,可以求解线性规划、整数规划、混合整数规划问题。
# 这一块内容是先学的,缝合了不少代码,细节可能有点问题,但能跑起来就行

def find_vertical_seam(energy):
N, M = energy.shape

# 初始化优化问题,给它起个名字
prob = pulp.LpProblem("Seam carving", pulp.LpMinimize)

# 创建变量
x = pulp.LpVariable.dicts("pixels",(list(range(N)),list(range(M))),0,1,pulp.LpInteger)

# 构建目标函数
objective_terms = list()
for i in range(N):
for j in range(M):
objective_terms.append(energy[i][j] * x[i][j])
prob += pulp.lpSum(objective_terms) # adds up all the terms in the list

# Constraint #1: 每行一个像素
for i in range(N):
prob += pulp.lpSum(x[i][j] for j in range(M)) == 1

# Constraint #2: 接缝的连通性
for i in range(N-1):
for j in range(M):
prob += pulp.lpSum([x[i][j]]+[-x[i+1][k] for k in range(max(0,j-1),min(M,j+2))]) <= 0

prob.solve()

# 通过收集最佳接缝中的像素来构建接缝
# 可以使用 pulp.value(x[i][j]) 访问变量的值(0 或 1)

seam = []
for i in range(N):
for j in range(M):
if pulp.value(x[i][j]) == 1.0:
seam.append(j)
return seam

再次测试

1
2
3
4
5
6
7
8
9
10
11
12
# 重复上述测试,可以看到线性规划比纯递归快了很多
np.random.seed(1)
h = 31
w = 31
img = np.random.randint(0,255,(h,w,3)).astype("uint8")
print(img.shape)
plt.figure()
plt.imshow(img)
e = energy(img)
img3 = seam_carve(img, h, w-1)
plt.figure()
plt.imshow(img3)

使用动态规划实现图像压缩

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
def find_vertical_seam(energy):
row = energy.shape[0]
col = energy.shape[1]
dp_energy = []
dp_energy.append(energy[0].tolist())

for i in range(1, row):
temp = []
for j in range(col):
if j == 0:
temp.append(energy[i][j] + min(dp_energy[i-1][j], dp_energy[i-1][j+1]))
elif j == col - 1:
temp.append(energy[i][j] + min(dp_energy[i-1][j], dp_energy[i-1][j-1]))
else:
temp.append(energy[i][j] + min(dp_energy[i-1][j-1], dp_energy[i-1][j], dp_energy[i-1][j+1]))
dp_energy.append(temp)

seam = [0] * row
seam[row-1] = np.argmin(dp_energy[row-1])
for i in range(row-2, -1, -1):
j = seam[i+1]
if j == 0:
seam[i] = np.argmin(dp_energy[i][j:j+2]) + j
elif j == col - 1:
seam[i] = np.argmin(dp_energy[i][j-1:j+1]) + j-1
else:
seam[i] = np.argmin(dp_energy[i][j-1:j+2]) + j-1
return seam
# 时间比较久了,这个我真的想不起来是怎么算的了,大概思路就是一边递归,一边求最优

对应的测试案例

1
2
3
4
5
6
7
8
9
10
11
np.random.seed(1)
h = 13
w = 13
img = np.random.randint(0,255,(h,w,3)).astype("uint8")
print(img.shape)
plt.figure()
plt.imshow(img)
e = energy(img)
img2 = seam_carve(img, h, w-1)
plt.figure()
plt.imshow(img2)