Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions exercises/contour_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,31 @@ def contour_detection(image_path):
返回:
tuple: (绘制轮廓的图像, 轮廓列表) 或 (None, None) 失败时
"""
# 请在此处编写代码
# 提示:
# 1. 使用 cv2.imread() 读取图像。
# 2. 检查图像是否成功读取。
# 3. 使用 cv2.cvtColor() 转为灰度图。
# 4. 使用 cv2.threshold() 进行二值化处理。
# 5. 使用 cv2.findContours() 检测轮廓 (注意不同 OpenCV 版本的返回值)。
# 6. 创建图像副本 img.copy() 用于绘制。
# 7. 使用 cv2.drawContours() 在副本上绘制轮廓。
# 8. 返回绘制后的图像和轮廓列表。
# 9. 使用 try...except 处理异常。
pass
try:
# 读取图像
img = cv2.imread(image_path)
if img is None:
return None, None

# 转为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化处理
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 检测轮廓(处理不同OpenCV版本的返回值差异)
contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 将轮廓转换为列表(如果还不是列表)
contour_list = list(contours) if not isinstance(contours, list) else contours

# 创建图像副本用于绘制
result_img = img.copy()

# 绘制轮廓
cv2.drawContours(result_img, contour_list, -1, (0, 255, 0), 2)

return result_img, contour_list

except Exception as e:
return None, None
30 changes: 20 additions & 10 deletions exercises/conv.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,23 @@ def conv2d(x, kernel):
out_H = H - kH + 1
out_W = W - kW + 1
"""
# 请在此处编写代码
# 提示:
# 1. 获取输入 x 和卷积核 kernel 的形状。
# 2. 计算输出的高度和宽度。
# 3. 初始化输出数组。
# 4. 使用嵌套循环遍历输出数组的每个位置 (i, j)。
# 5. 提取输入 x 中与当前卷积核对应的区域 (patch)。
# 6. 计算 patch 和 kernel 的元素乘积之和 (np.sum(patch * kernel))。
# 7. 将结果存入输出数组 out[i, j]。
pass
# 获取输入和卷积核的形状
H, W = x.shape
kH, kW = kernel.shape

# 计算输出形状
out_H = H - kH + 1
out_W = W - kW + 1

# 初始化输出数组
out = np.zeros((out_H, out_W))

# 遍历输出数组的每个位置
for i in range(out_H):
for j in range(out_W):
# 提取输入中与当前卷积核对应的区域
patch = x[i:i+kH, j:j+kW]
# 计算点乘并求和
out[i, j] = np.sum(patch * kernel)

return out
26 changes: 15 additions & 11 deletions exercises/cross_entropy.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@ def cross_entropy_loss(y_true, y_pred):
Return:
float: 平均交叉熵损失。
"""
# 请在此处编写代码
# 提示:
# 1. 获取样本数量 N 和类别数量 C。
# 2. 如果 y_true 是类别索引 (形状为 (N,)), 将其转换为独热编码 (形状为 (N, C))。
# (可以使用 np.eye(C)[y_true] 或类似方法)。
# 3. 为防止 log(0) 错误,将 y_pred 中非常小的值替换为一个小的正数 (如 1e-12),
# 可以使用 np.clip(y_pred, 1e-12, 1.0)。
# 4. 计算交叉熵损失:L = - sum(y_true * log(y_pred))。
# 在 NumPy 中是 -np.sum(y_true * np.log(y_pred))。
# 5. 计算所有样本的平均损失:L / N。
pass
# 获取样本数量 N 和类别数量 C
N = y_pred.shape[0]
C = y_pred.shape[1]

# 如果 y_true 是类别索引 (形状为 (N,)), 将其转换为独热编码
if y_true.ndim == 1:
y_true = np.eye(C)[y_true]

# 防止 log(0) 错误,将 y_pred 中非常小的值替换为一个小的正数
y_pred = np.clip(y_pred, 1e-12, 1.0)

# 计算交叉熵损失
loss = -np.sum(y_true * np.log(y_pred)) / N

return loss
28 changes: 19 additions & 9 deletions exercises/image_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,22 @@ def image_processing_pipeline(image_path):
edges: Canny 边缘检测的结果 (NumPy 数组, 灰度图像).
如果读取图像失败, 返回 None.
"""
# 请在此处编写代码
# 提示:
# 1. 使用 cv2.imread() 读取图像。
# 2. 检查图像是否成功读取(img is None?)。
# 3. 使用 cv2.cvtColor() 将图像转为灰度图 (cv2.COLOR_BGR2GRAY)。
# 4. 使用 cv2.GaussianBlur() 进行高斯滤波。
# 5. 使用 cv2.Canny() 进行边缘检测。
# 6. 使用 try...except 包裹代码以处理可能的异常。
pass
try:
# 读取图像
img = cv2.imread(image_path)
if img is None:
return None

# 转为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 高斯滤波
blurred = cv2.GaussianBlur(gray, (5, 5), 1.5)

# Canny边缘检测
edges = cv2.Canny(blurred, 50, 150)

return edges

except Exception as e:
return None
37 changes: 20 additions & 17 deletions exercises/iou.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,23 @@ def calculate_iou(box1, box2):
Return:
float: 计算得到的 IoU 值,范围在 [0, 1]。
"""
# 请在此处编写代码
# 提示:
# 1. 确定两个框相交区域的左上角坐标 (x_left, y_top)。
# x_left = max(box1[0], box2[0])
# y_top = max(box1[1], box2[1])
# 2. 确定两个框相交区域的右下角坐标 (x_right, y_bottom)。
# x_right = min(box1[2], box2[2])
# y_bottom = min(box1[3], box2[3])
# 3. 计算相交区域的面积 intersection_area。
# 注意处理不相交的情况 (宽度或高度 <= 0)。
# intersection_area = max(0, x_right - x_left) * max(0, y_bottom - y_top)
# 4. 计算 box1 的面积 box1_area。
# 5. 计算 box2 的面积 box2_area。
# 6. 计算并集面积 union_area = box1_area + box2_area - intersection_area。
# 7. 计算 IoU = intersection_area / union_area。
# 注意处理 union_area 为 0 的情况 (除零错误)。
pass
# 确定相交区域的坐标
x_left = max(box1[0], box2[0])
y_top = max(box1[1], box2[1])
x_right = min(box1[2], box2[2])
y_bottom = min(box1[3], box2[3])

# 计算相交区域面积
intersection_area = max(0, x_right - x_left) * max(0, y_bottom - y_top)

# 计算各自面积
box1_area = (box1[2] - box1[0]) * (box1[3] - box1[1])
box2_area = (box2[2] - box2[0]) * (box2[3] - box2[1])

# 计算并集面积
union_area = box1_area + box2_area - intersection_area

# 计算IoU,处理除零情况
iou = intersection_area / union_area if union_area > 0 else 0.0

return iou
10 changes: 4 additions & 6 deletions exercises/leaky_relu.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ def leaky_relu(x, alpha=0.01):
Return:
np.array: Leaky ReLU 激活后的数组,形状与输入相同。
"""
# 请在此处编写代码
# 提示:
# 1. 可以使用 np.maximum() 函数。
# 2. 计算 alpha * x。
# 3. 计算 max(alpha * x, x)。
pass
# 计算alpha * x
alpha_x = alpha * x
# 计算max(alpha * x, x)
return np.maximum(alpha_x, x)
34 changes: 24 additions & 10 deletions exercises/maxpool.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,27 @@ def maxpool(x, kernel_size, stride):
out_H = (H - kernel_size) // stride + 1
out_W = (W - kernel_size) // stride + 1
"""
# 请在此处编写代码
# 提示:
# 1. 计算输出的高度和宽度。
# 2. 初始化输出数组。
# 3. 使用嵌套循环遍历输出数组的每个位置 (i, j)。
# 4. 计算当前池化窗口在输入数组 x 中的起始位置 (h_start, w_start)。
# 5. 提取当前池化窗口 window = x[h_start:h_start+kernel_size, w_start:w_start+kernel_size]。
# 6. 找到窗口中的最大值 np.max(window)。
# 7. 将最大值存入输出数组 out[i, j]。
pass
# 获取输入尺寸
H, W = x.shape

# 计算输出尺寸
out_H = (H - kernel_size) // stride + 1
out_W = (W - kernel_size) // stride + 1

# 初始化输出数组
out = np.zeros((out_H, out_W))

# 遍历输出数组的每个位置
for i in range(out_H):
for j in range(out_W):
# 计算当前窗口在输入中的起始位置
h_start = i * stride
w_start = j * stride

# 提取当前窗口
window = x[h_start:h_start+kernel_size, w_start:w_start+kernel_size]

# 计算窗口中的最大值并存入输出
out[i, j] = np.max(window)

return out
64 changes: 45 additions & 19 deletions exercises/nms.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,24 @@ def calculate_iou(box1, box2):
Return:
float: IoU 值。
"""
# 请在此处编写代码
# (与 iou.py 中的练习相同,可以复用代码或导入)
# 提示:计算交集面积和并集面积,然后相除。
pass
# 计算相交区域坐标
x_left = max(box1[0], box2[0])
y_top = max(box1[1], box2[1])
x_right = min(box1[2], box2[2])
y_bottom = min(box1[3], box2[3])

# 计算相交区域面积
intersection_area = max(0, x_right - x_left) * max(0, y_bottom - y_top)

# 计算各自面积
box1_area = (box1[2] - box1[0]) * (box1[3] - box1[1])
box2_area = (box2[2] - box2[0]) * (box2[3] - box2[1])

# 计算并集面积和IoU
union_area = box1_area + box2_area - intersection_area
iou = intersection_area / union_area if union_area > 0 else 0.0

return iou

def nms(boxes, scores, iou_threshold):
"""
Expand All @@ -38,18 +52,30 @@ def nms(boxes, scores, iou_threshold):
Return:
list: 保留下来(未被抑制)的边界框的索引列表。
"""
# 请在此处编写代码
# 提示:
# 1. 如果 boxes 为空,直接返回空列表。
# 2. 将 boxes 和 scores 转换为 NumPy 数组。
# 3. 计算所有边界框的面积 areas。
# 4. 根据 scores 对边界框索引进行降序排序 (order = np.argsort(scores)[::-1])。
# 5. 初始化一个空列表 keep 用于存储保留的索引。
# 6. 当 order 列表不为空时循环:
# a. 取出 order 中的第一个索引 i (当前分数最高的框),加入 keep。
# b. 计算框 i 与 order 中剩余所有框的 IoU。
# (需要计算交集区域坐标 xx1, yy1, xx2, yy2 和交集面积 intersection)
# c. 找到 IoU 小于等于 iou_threshold 的索引 inds。
# d. 更新 order,只保留那些 IoU <= threshold 的框的索引 (order = order[inds + 1])。
# 7. 返回 keep 列表。
pass
# 处理空输入
if len(boxes) == 0:
return []

# 转换为NumPy数组
boxes = np.array(boxes)
scores = np.array(scores)

# 根据分数降序排序
order = np.argsort(scores)[::-1]

keep = []
while order.size > 0:
# 取出当前最高分的框
i = order[0]
keep.append(i)

# 计算与剩余框的IoU
ious = np.array([calculate_iou(boxes[i], boxes[j]) for j in order[1:]])

# 找到IoU小于阈值的框
inds = np.where(ious <= iou_threshold)[0]

# 更新order,保留符合条件的框
order = order[inds + 1]

return keep
25 changes: 17 additions & 8 deletions exercises/smooth_l1.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,20 @@ def smooth_l1(x, sigma=1.0):
Return:
np.array: 计算得到的 Smooth L1 损失数组,形状与输入相同。
"""
# 请在此处编写代码
# 提示:
# 1. 计算 sigma 的平方 sigma2。
# 2. 找到满足条件 |x| < 1 / sigma2 的元素索引 (可以使用 np.abs 和比较运算符)。
# 3. 对满足条件的元素应用第一个公式 (0.5 * (sigma * x)**2)。
# 4. 对不满足条件的元素应用第二个公式 (|x| - 0.5 / sigma2)。
# 5. 可以使用 np.where() 来根据条件选择应用哪个公式。
pass
# 计算sigma的平方
sigma2 = sigma ** 2

# 计算阈值
threshold = 1.0 / sigma2

# 计算绝对值
abs_x = np.abs(x)

# 应用不同公式
loss = np.where(
abs_x < threshold,
0.5 * (sigma * x) ** 2,
abs_x - 0.5 / sigma2
)

return loss