YOLOv8目标检测实战:从环境配置到工业部署完整指南
在深度学习目标检测领域YOLO系列算法一直以其出色的实时性和准确性备受关注。最近在部署YOLOv8进行工业质检项目时我发现网上教程虽然多但要么版本过时要么步骤不完整特别是自定义数据集训练环节存在大量坑点。本文基于最新2026年环境整合一套从零开始的完整实操方案包含环境配置、数据集准备、模型训练全流程每个步骤都经过实际验证适合计算机视觉入门者和项目开发者直接复用。1. YOLOv8核心概念与优势解析1.1 什么是YOLOv8目标检测算法YOLOv8是Ultralytics公司推出的最新一代目标检测模型基于先前YOLO版本的优化改进在精度和速度之间实现了更好的平衡。与传统的两阶段检测算法不同YOLOv8采用单阶段检测架构将目标检测任务转化为回归问题直接在图像网格中预测边界框和类别概率。YOLOv8的核心创新在于其骨干网络和neck部分的优化。模型采用了更高效的CSPDarknet53作为骨干网络结合SPPFSpatial Pyramid Pooling Fast模块和PAN-FPNPath Aggregation Network Feature Pyramid Network结构能够在不同尺度上有效提取和融合特征。这种设计使得YOLOv8在处理不同大小目标时都表现出色特别适合现实场景中多尺度目标的检测需求。1.2 YOLOv8相比前代产品的优势YOLOv8在多个方面相比YOLOv5有显著提升。首先在精度方面YOLOv8在COCO数据集上的mAP平均精度比YOLOv5提升约5-10%这主要得益于更好的网络结构和训练策略。其次在推理速度上YOLOv8通过优化模型结构和计算流程在相同硬件条件下能够实现更快的推理速度。另一个重要改进是YOLOv8提供了更加友好的API接口和更完善的文档支持。Ultralytics公司为YOLOv8提供了Python包管理安装方式大大简化了环境配置流程。同时YOLOv8支持多种任务模式包括目标检测、实例分割、姿态估计等为不同应用场景提供了统一解决方案。1.3 YOLOv8的典型应用场景YOLOv8在实际项目中有着广泛的应用前景。在工业质检领域可以用于检测产品缺陷、识别错漏装等问题在安防监控中能够实时检测人员、车辆等目标在自动驾驶场景下可用于道路障碍物识别和交通标志检测在医疗影像分析中能够辅助医生定位病灶区域。此外在零售、农业、无人机巡检等多个行业都有成功应用案例。2. 环境准备与依赖安装2.1 硬件与系统要求YOLOv8对硬件环境有一定的要求但同时也提供了多种配置选项以适应不同资源条件。对于GPU环境推荐使用NVIDIA GTX 1660及以上显卡显存至少6GB。CPU环境下也能运行但训练速度会显著下降。内存建议16GB以上存储空间需要预留20GB用于安装依赖和存储数据集。操作系统方面YOLOv8支持Windows 10/11、LinuxUbuntu 16.04及以上、macOS10.14及以上。本文以Windows 11系统为例进行演示其他系统操作类似主要差异在于包管理工具和路径表示。2.2 Python环境配置首先需要安装Python 3.8或以上版本。推荐使用Miniconda或Anaconda创建独立的Python环境避免与系统其他Python项目产生冲突。# 创建名为yolov8的conda环境 conda create -n yolov8 python3.9 conda activate yolov8 # 验证Python版本 python --version2.3 核心依赖安装YOLOv8的核心依赖包括PyTorch、Ultralytics包以及其他计算机视觉库。以下是完整的安装命令# 安装PyTorch根据CUDA版本选择 # 如果有CUDA 11.7 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117 # 如果只有CPU pip install torch torchvision torchaudio # 安装Ultralytics YOLOv8 pip install ultralytics # 安装其他辅助库 pip install opencv-python pillow matplotlib seaborn pandas numpy2.4 环境验证安装完成后需要验证环境是否配置正确# 验证脚本test_environment.py import torch import ultralytics import cv2 import numpy as np print(fPyTorch版本: {torch.__version__}) print(fCUDA是否可用: {torch.cuda.is_available()}) print(fCUDA版本: {torch.version.cuda}) print(fUltralytics版本: {ultralytics.__version__}) # 测试GPU if torch.cuda.is_available(): device torch.device(cuda) print(f使用GPU: {torch.cuda.get_device_name(0)}) else: device torch.device(cpu) print(使用CPU) # 测试YOLOv8基础功能 from ultralytics import YOLO print(YOLOv8环境验证通过)运行上述脚本应该能够正常输出各组件版本信息且不报错。如果遇到CUDA相关错误需要检查显卡驱动和CUDA工具包安装情况。3. YOLOv8模型结构与配置详解3.1 模型架构深度解析YOLOv8的模型架构经过精心设计在保持高效性的同时提升了检测精度。整个网络可以分为三个主要部分骨干网络Backbone、颈部网络Neck和检测头Head。骨干网络采用CSPDarknet53结构通过跨阶段局部连接减少了计算量的同时保持了梯度流。该网络使用了多个CBSConv-BN-SiLU模块和C2fCross Stage Partial bottleneck with 2 convolutions模块这些模块通过残差连接和特征重用提高了特征提取效率。颈部网络采用PAN-FPN结构通过自上而下和自下而上的特征金字塔融合将深层语义信息与浅层位置信息有效结合。这种多尺度特征融合机制使模型能够同时检测大目标和小目标提升了模型在不同尺度目标上的表现。3.2 模型配置文件解读YOLOv8使用YAML格式的配置文件定义模型结构以下是一个简化的配置文件示例# YOLOv8n模型配置文件示例 nc: 80 # 类别数量 scales: # 模型缩放系数 n: [0.33, 0.25, 1024] # depth, width, max_channels s: [0.33, 0.50, 1024] m: [0.67, 0.75, 1024] l: [1.00, 1.00, 1024] x: [1.00, 1.25, 1024] # 骨干网络配置 backbone: # [from, repeats, module, args] - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2 - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4 - [-1, 3, C2f, [128, True]] - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8 - [-1, 6, C2f, [256, True]] - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16 - [-1, 6, C2f, [512, True]] - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32 - [-1, 3, C2f, [1024, True]] - [-1, 1, SPPF, [1024, 5]] # 9 # 颈部网络配置 head: - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 6], 1, Concat, [1]] # cat backbone P4 - [-1, 3, C2f, [512]] # 12 - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 4], 1, Concat, [1]] # cat backbone P3 - [-1, 3, C2f, [256]] # 15 (P3/8-small) - [-1, 1, Conv, [256, 3, 2]] - [[-1, 12], 1, Concat, [1]] # cat head P4 - [-1, 3, C2f, [512]] # 18 (P4/16-medium) - [-1, 1, Conv, [512, 3, 2]] - [[-1, 9], 1, Concat, [1]] # cat head P5 - [-1, 3, C2f, [1024]] # 21 (P5/32-large) - [[15, 18, 21], 1, Detect, [nc]] # Detect(P3, P4, P5)3.3 不同尺寸模型选择策略YOLOv8提供了多种尺寸的预训练模型从轻量级的YOLOv8n到高精度的YOLOv8x用户可以根据实际需求选择合适的模型YOLOv8n参数量最小速度最快适合移动端或资源受限环境YOLOv8s平衡型模型在速度和精度间取得较好平衡YOLOv8m中等规模适合大多数工业应用场景YOLOv8l大规模模型精度较高需要更多计算资源YOLOv8x最大模型精度最高适合对检测精度要求极高的场景选择策略建议从YOLOv8s开始尝试如果速度满足要求但精度不足可以升级到更大模型如果速度不满足要求则降级到更小模型。4. 自定义数据集准备与标注4.1 数据集目录结构规范正确的数据集结构是成功训练的基础。YOLOv8要求的数据集结构如下datasets/ └── custom/ ├── images/ │ ├── train/ │ │ ├── image1.jpg │ │ ├── image2.jpg │ │ └── ... │ └── val/ │ ├── image101.jpg │ ├── image102.jpg │ └── ... └── labels/ ├── train/ │ ├── image1.txt │ ├── image2.txt │ └── ... └── val/ ├── image101.txt ├── image102.txt └── ...训练集和验证集建议按8:2或7:3的比例划分。图像格式支持JPG、PNG等常见格式标注文件为TXT格式每个图像对应一个同名的标注文件。4.2 数据标注标准与工具使用YOLOv8使用YOLO格式的标注每个标注文件包含多行每行代表一个目标对象的标注信息格式为class_id x_center y_center width height其中坐标和尺寸都是相对于图像宽度和高度的归一化值0-1之间。推荐使用LabelImg或LabelStudio进行标注# 安装LabelImg pip install labelImg labelImg # 启动标注工具标注时需要注意以下几点边界框应紧贴目标边缘但不要过紧对于遮挡目标按可见部分标注确保每个目标都有正确的类别标签标注完成后检查标注质量避免漏标错标4.3 数据集配置文件创建创建数据集配置文件dataset.yaml内容如下# dataset.yaml path: /path/to/datasets/custom # 数据集根目录 train: images/train # 训练集图像路径 val: images/val # 验证集图像路径 test: images/test # 测试集图像路径可选 # 类别列表 names: 0: person 1: car 2: traffic_light 3: stop_sign # 添加更多类别... # 类别数量 nc: 4 # 下载命令/URL可选 download: None这个配置文件将用于训练时指定数据集路径和类别信息。4.4 数据增强与预处理YOLOv8内置了丰富的数据增强策略包括# 数据增强配置示例 augmentation_config { hsv_h: 0.015, # 色调增强 hsv_s: 0.7, # 饱和度增强 hsv_v: 0.4, # 亮度增强 degrees: 0.0, # 旋转角度 translate: 0.1, # 平移 scale: 0.5, # 缩放 shear: 0.0, # 剪切 perspective: 0.0, # 透视变换 flipud: 0.0, # 上下翻转 fliplr: 0.5, # 左右翻转 mosaic: 1.0, # 马赛克增强 mixup: 0.0, # MixUp增强 }这些增强策略可以在训练配置中调整以适应不同的数据集特性。5. 模型训练完整流程5.1 训练参数配置详解YOLOv8训练涉及多个重要参数需要根据具体任务进行调整from ultralytics import YOLO # 加载预训练模型 model YOLO(yolov8n.pt) # 可以选择yolov8s.pt、yolov8m.pt等 # 训练配置 training_config { data: dataset.yaml, # 数据集配置文件 epochs: 100, # 训练轮数 imgsz: 640, # 图像尺寸 batch: 16, # 批次大小 device: 0, # 使用GPU 0如果是CPU则设为cpu workers: 8, # 数据加载线程数 optimizer: auto, # 优化器自动选择 lr0: 0.01, # 初始学习率 lrf: 0.01, # 最终学习率 momentum: 0.937, # 动量 weight_decay: 0.0005, # 权重衰减 warmup_epochs: 3.0, # 热身轮数 box: 7.5, # 框损失权重 cls: 0.5, # 分类损失权重 dfl: 1.5, # DFL损失权重 close_mosaic: 10, # 最后10轮关闭马赛克增强 }5.2 单GPU训练实战对于大多数个人开发者单GPU训练是最常见的场景# 单GPU训练完整示例 from ultralytics import YOLO def train_yolov8_single_gpu(): # 加载模型 model YOLO(yolov8n.pt) # 开始训练 results model.train( datadataset.yaml, epochs100, imgsz640, batch16, device0, # 使用第一个GPU workers8, patience50, # 早停耐心值 saveTrue, exist_okTrue, # 允许覆盖现有训练结果 projectyolov8_training, namecustom_model_v1, valTrue, # 开启验证 plotsTrue # 生成训练曲线图 ) return results if __name__ __main__: results train_yolov8_single_gpu() print(训练完成)5.3 多GPU分布式训练对于大规模数据集或需要快速迭代的场景可以使用多GPU训练# 多GPU训练示例 from ultralytics import YOLO def train_yolov8_multi_gpu(): model YOLO(yolov8n.pt) results model.train( datadataset.yaml, epochs100, imgsz640, batch64, # 多GPU可以增大batch size device[0, 1, 2, 3], # 使用4个GPU workers16, patience30, projectyolov8_multi_gpu, namemulti_gpu_training ) return results # 或者使用自动选择空闲GPU def train_yolov8_auto_gpu(): model YOLO(yolov8n.pt) results model.train( datadataset.yaml, epochs100, imgsz640, device[-1, -1], # 自动选择2个最空闲的GPU # 其他参数... )5.4 训练过程监控与调优训练过程中需要密切关注各项指标# 训练监控工具函数 import matplotlib.pyplot as plt import pandas as pd def monitor_training_results(results_path): 监控训练结果并生成可视化报告 # 读取训练结果CSV文件 results_csv f{results_path}/results.csv df pd.read_csv(results_csv) # 创建监控图表 fig, axes plt.subplots(2, 2, figsize(15, 10)) # 损失曲线 axes[0, 0].plot(df[epoch], df[train/box_loss], labelBox Loss) axes[0, 0].plot(df[epoch], df[train/cls_loss], labelCls Loss) axes[0, 0].plot(df[epoch], df[train/dfl_loss], labelDFL Loss) axes[0, 0].set_title(Training Loss) axes[0, 0].legend() # 验证指标 axes[0, 1].plot(df[epoch], df[metrics/precision(B)], labelPrecision) axes[0, 1].plot(df[epoch], df[metrics/recall(B)], labelRecall) axes[0, 1].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) axes[0, 1].plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) axes[0, 1].set_title(Validation Metrics) axes[0, 1].legend() # 学习率变化 axes[1, 0].plot(df[epoch], df[lr/pg0], labelLearning Rate) axes[1, 0].set_title(Learning Rate Schedule) plt.tight_layout() plt.savefig(training_monitor.png, dpi300, bbox_inchestight) plt.show() # 使用TensorBoard进行实时监控 # tensorboard --logdir yolov8_training6. 模型验证与性能评估6.1 验证集评估指标解读训练完成后需要对模型进行全面的性能评估from ultralytics import YOLO import matplotlib.pyplot as plt def evaluate_model(model_path, data_config): 全面评估模型性能 # 加载训练好的模型 model YOLO(model_path) # 在验证集上评估 metrics model.val( datadata_config, splitval, # 使用验证集 imgsz640, batch16, conf0.25, # 置信度阈值 iou0.6, # IoU阈值 device0 ) # 打印详细指标 print( 模型评估结果 ) print(f精确度 (Precision): {metrics.box.p:.4f}) print(f召回率 (Recall): {metrics.box.r:.4f}) print(fmAP0.5: {metrics.box.map50:.4f}) print(fmAP0.5:0.95: {metrics.box.map:.4f}) return metrics # 执行评估 trained_model_path yolov8_training/custom_model_v1/weights/best.pt metrics evaluate_model(trained_model_path, dataset.yaml)6.2 混淆矩阵与PR曲线分析深入分析模型在不同类别上的表现def detailed_analysis(model_path, data_config): 详细性能分析 model YOLO(model_path) # 生成混淆矩阵 model.val( datadata_config, plotsTrue, # 生成各种分析图表 save_jsonTrue, # 保存JSON格式结果 save_hybridTrue # 保存混合结果 ) # 分析类别级别的性能 class_metrics {} for class_id, class_name in data_config[names].items(): # 可以针对每个类别进行详细分析 class_metrics[class_name] { precision: 0.0, # 实际从metrics中获取 recall: 0.0, ap: 0.0 } return class_metrics6.3 可视化检测结果对验证集样本进行可视化检查def visualize_detections(model_path, image_dir, output_dir): 可视化检测结果 import os import cv2 from ultralytics import YOLO model YOLO(model_path) # 创建输出目录 os.makedirs(output_dir, exist_okTrue) # 处理验证集图像 image_files [f for f in os.listdir(image_dir) if f.endswith((.jpg, .png))] for image_file in image_files[:10]: # 只处理前10张作为示例 image_path os.path.join(image_dir, image_file) # 进行预测 results model.predict( sourceimage_path, conf0.25, iou0.6, imgsz640, saveFalse # 不自动保存我们自己处理 ) # 绘制检测结果 for r in results: im_array r.plot() # 绘制边界框和标签 im_bgr cv2.cvtColor(im_array, cv2.COLOR_RGB2BGR) # 保存结果图像 output_path os.path.join(output_dir, fresult_{image_file}) cv2.imwrite(output_path, im_bgr) print(f可视化结果已保存到: {output_dir}) # 使用示例 visualize_detections( model_pathbest.pt, image_dirdatasets/custom/images/val, output_dirdetection_results )7. 模型导出与部署7.1 模型格式转换YOLOv8支持导出多种格式以适应不同部署环境from ultralytics import YOLO def export_model(model_path, export_formats): 导出模型为不同格式 model YOLO(model_path) for format in export_formats: try: # 导出模型 model.export( formatformat, imgsz640, optimizeTrue, # 优化推理速度 int8False, # 是否使用INT8量化 device0 ) print(f成功导出为 {format.upper()} 格式) except Exception as e: print(f导出 {format} 格式失败: {e}) # 支持的导出格式 formats [ torchscript, # TorchScript onnx, # ONNX openvino, # OpenVINO engine, # TensorRT coreml, # CoreML (苹果设备) saved_model, # TensorFlow SavedModel pb, # TensorFlow GraphDef tflite, # TensorFlow Lite edgetpu, # Edge TPU paddle, # PaddlePaddle ] export_model(best.pt, formats[onnx, engine])7.2 ONNX格式导出详解ONNX是目前最通用的模型交换格式def export_to_onnx_detailed(model_path): 详细配置ONNX导出 model YOLO(model_path) # ONNX导出配置 success model.export( formatonnx, imgsz640, halfFalse, # 是否使用FP16 dynamicFalse, # 动态输入尺寸 simplifyTrue, # 简化模型 opset12, # ONNX算子集版本 batch1, # 批次大小 devicecpu # 导出设备 ) if success: print(ONNX导出成功) # 验证导出的ONNX模型 import onnx onnx_model onnx.load(best.onnx) onnx.checker.check_model(onnx_model) print(ONNX模型验证通过) else: print(ONNX导出失败) export_to_onnx_detailed(best.pt)7.3 TensorRT加速部署对于需要极致推理速度的场景可以使用TensorRTdef export_to_tensorrt(model_path): 导出为TensorRT引擎 model YOLO(model_path) success model.export( formatengine, imgsz640, halfTrue, # 使用FP16加速 device0, workspace4, # GPU内存 workspace (GB) verboseFalse ) if success: print(TensorRT引擎导出成功) # 测试TensorRT推理速度 trt_model YOLO(best.engine) results trt_model(test_image.jpg) print(TensorRT推理测试完成) return success8. 实际应用与推理部署8.1 Python推理接口使用YOLOv8提供了简洁的Python推理接口from ultralytics import YOLO import cv2 import numpy as np class YOLOv8Detector: YOLOv8检测器封装类 def __init__(self, model_path, conf_threshold0.25, iou_threshold0.7): self.model YOLO(model_path) self.conf_threshold conf_threshold self.iou_threshold iou_threshold def detect_image(self, image_path, save_resultTrue): 检测单张图像 results self.model.predict( sourceimage_path, confself.conf_threshold, iouself.iou_threshold, imgsz640, savesave_result ) return results def detect_video(self, video_path, output_pathNone): 检测视频流 cap cv2.VideoCapture(video_path) if output_path: fourcc cv2.VideoWriter_fourcc(*mp4v) out cv2.VideoWriter(output_path, fourcc, 30.0, (int(cap.get(3)), int(cap.get(4)))) while cap.isOpened(): ret, frame cap.read() if not ret: break # 进行检测 results self.model(frame) annotated_frame results[0].plot() if output_path: out.write(annotated_frame) else: cv2.imshow(Detection, annotated_frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() if output_path: out.release() cv2.destroyAllWindows() def detect_webcam(self, camera_id0): 实时摄像头检测 cap cv2.VideoCapture(camera_id) while True: ret, frame cap.read() if not ret: break results self.model(frame) annotated_frame results[0].plot() cv2.imshow(Webcam Detection, annotated_frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() # 使用示例 detector YOLOv8Detector(best.pt) results detector.detect_image(test_image.jpg)8.2 批量处理与性能优化对于需要处理大量图像的场景import os from tqdm import tqdm import time class BatchProcessor: 批量图像处理器 def __init__(self, model_path, input_dir, output_dir): self.detector YOLOv8Detector(model_path) self.input_dir input_dir self.output_dir output_dir os.makedirs(output_dir, exist_okTrue) def process_batch(self, batch_size32): 批量处理图像 image_files [f for f in os.listdir(self.input_dir) if f.endswith((.jpg, .png, .jpeg))] total_time 0 processed_count 0 for i in tqdm(range(0, len(image_files), batch_size)): batch_files image_files[i:ibatch_size] batch_paths [os.path.join(self.input_dir, f) for f in batch_files] start_time time.time() # 批量处理 results self.detector.model(batch_paths) batch_time time.time() - start_time total_time batch_time processed_count len(batch_files) # 保存结果 for j, result in enumerate(results): output_path os.path.join(self.output_dir, fresult_{batch_files[j]}) result.save(filenameoutput_path) # 性能统计 avg_time total_time / processed_count fps 1.0 / avg_time if avg_time 0 else 0 print(f处理完成: {processed_count} 张图像) print(f平均处理时间: {avg_time:.4f} 秒/张) print(f推理速度: {fps:.2f} FPS) return processed_count, avg_time, fps # 使用示例 processor BatchProcessor(best.pt, input_images, output_results) count, avg_time, fps processor.process_batch(batch_size16)9. 常见问题与解决方案9.1 训练过程中的典型问题问题1CUDA内存不足OOM解决方案# 减少批次大小 training_config { batch: 8, # 从16减少到8 imgsz: 640, # 或减小图像尺寸 # ... 其他配置 } # 使用梯度累积 training_config[accumulate] 2 # 每2个批次更新一次权重问题2训练损失不收敛解决方案# 调整学习率策略 training_config { lr0: 0.001, # 降低初始学习率 lrf: 0.1, # 调整最终学习率比例 warmup_epochs: 5, # 增加热身轮数 optimizer: AdamW, # 更换优化器 }问题3过拟合解决方案# 增加正则化 training_config { weight_decay: 0.001, # 增加权重衰减 dropout: 0.2, # 添加dropout patience: 20, # 早停 data_augmentation: { # 增强数据增强 hsv_h: 0.02, hsv_s: 0.8, hsv_v: 0.5, fliplr: 0.5, } }9.2 推理部署问题排查问题模型推理速度慢优化方案# 1. 使用更小的模型 model YOLO(yolov8n.pt) # 而不是yolov8x.pt # 2. 使用半精度推理 results model.predict(sourceimage.jpg, halfTrue) # 3. 优化图像尺寸 results model.predict(sourceimage.jpg, imgsz320) # 减小尺寸 # 4. 使用TensorRT加速 model YOLO(best.engine) # 使用导出的TensorRT引擎问题检测精度不足提升方案# 1. 调整置信度阈值 results model.predict(sourceimage.jpg, conf0.1) # 降低阈值检测更多目标 # 2. 调整NMS阈值 results model.predict(sourceimage.jpg, iou0.4) # 降低IoU阈值 # 3. 使用测试时增强 results model.predict(sourceimage.jpg, augmentTrue)9.3 数据集相关问题问题类别不平衡解决方案# 使用类别权重 training_config { cls_pw: 1.0, # 应用类别权重 # 或者在数据层面处理 oversample: True, # 对少数类别过采样 undersample: False, # 不对多数类别欠采样 }问题标注质量不佳改善措施# 数据清洗和验证脚本 def validate_annotations(annotations_dir, images_dir): 验证标注文件质量 import os from PIL import Image issues [] for ann_file in os.listdir(annotations_dir): if not ann_file.endswith(.txt): continue # 检查对应的图像文件是否存在 image_file ann_file.replace(.txt, .jpg) image_path os.path.join(images_dir, image_file) if not os.path.exists(image_path): issues.append(f缺失图像文件: {image_file}) continue # 检查标注文件内容 ann_path os.path.join(annotations_dir, ann_file) with open(ann_path, r) as f: lines f.readlines() if len(lines) 0: