From 0c5b49f81f7a56eeeaab11221ba979fcd69695d3 Mon Sep 17 00:00:00 2001 From: johrrrn123 <1503987521@qq.com> Date: Sat, 24 May 2025 00:21:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=20if=5Felse.py=20=E7=BB=83?= =?UTF-8?q?=E4=B9=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- exercises/contour_detection.py | 40 ++++++++++++++------- exercises/conv.py | 30 ++++++++++------ exercises/cross_entropy.py | 26 ++++++++------ exercises/image_processing.py | 28 ++++++++++----- exercises/iou.py | 37 +++++++++++--------- exercises/leaky_relu.py | 10 +++--- exercises/maxpool.py | 34 ++++++++++++------ exercises/nms.py | 64 ++++++++++++++++++++++++---------- exercises/smooth_l1.py | 25 ++++++++----- 9 files changed, 192 insertions(+), 102 deletions(-) diff --git a/exercises/contour_detection.py b/exercises/contour_detection.py index 64ceb82..877bc1d 100644 --- a/exercises/contour_detection.py +++ b/exercises/contour_detection.py @@ -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 \ No newline at end of file + 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 \ No newline at end of file diff --git a/exercises/conv.py b/exercises/conv.py index 0d42079..6324de7 100644 --- a/exercises/conv.py +++ b/exercises/conv.py @@ -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 \ No newline at end of file + # 获取输入和卷积核的形状 + 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 diff --git a/exercises/cross_entropy.py b/exercises/cross_entropy.py index 6a70a16..f8c33fa 100644 --- a/exercises/cross_entropy.py +++ b/exercises/cross_entropy.py @@ -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 \ No newline at end of file + # 获取样本数量 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 \ No newline at end of file diff --git a/exercises/image_processing.py b/exercises/image_processing.py index e141a89..fc82695 100644 --- a/exercises/image_processing.py +++ b/exercises/image_processing.py @@ -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 \ No newline at end of file + 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 \ No newline at end of file diff --git a/exercises/iou.py b/exercises/iou.py index a9ed259..d63d1fe 100644 --- a/exercises/iou.py +++ b/exercises/iou.py @@ -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 \ No newline at end of file + # 确定相交区域的坐标 + 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 \ No newline at end of file diff --git a/exercises/leaky_relu.py b/exercises/leaky_relu.py index 6ee95a2..42a5d27 100644 --- a/exercises/leaky_relu.py +++ b/exercises/leaky_relu.py @@ -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 \ No newline at end of file + # 计算alpha * x + alpha_x = alpha * x + # 计算max(alpha * x, x) + return np.maximum(alpha_x, x) \ No newline at end of file diff --git a/exercises/maxpool.py b/exercises/maxpool.py index aceb96b..8efc8ef 100644 --- a/exercises/maxpool.py +++ b/exercises/maxpool.py @@ -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 \ No newline at end of file + # 获取输入尺寸 + 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 \ No newline at end of file diff --git a/exercises/nms.py b/exercises/nms.py index 4ece0bc..e050ac3 100644 --- a/exercises/nms.py +++ b/exercises/nms.py @@ -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): """ @@ -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 \ No newline at end of file + # 处理空输入 + 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 \ No newline at end of file diff --git a/exercises/smooth_l1.py b/exercises/smooth_l1.py index 7847a04..ab3d7a9 100644 --- a/exercises/smooth_l1.py +++ b/exercises/smooth_l1.py @@ -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 \ No newline at end of file + # 计算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 \ No newline at end of file