日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人工智能 > 目标检测 >内容正文

目标检测

【效率提高10倍项目原创发布!】深度学习数据自动标注器开源 目标检测和图像分类(高精度高效率)

發布時間:2024/8/1 目标检测 78 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【效率提高10倍项目原创发布!】深度学习数据自动标注器开源 目标检测和图像分类(高精度高效率) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 項目結構與使用教程
      • 目標檢測模式
      • 影像分類模式
    • 數據采集演示與訓練出來的模型演示
      • 訓練出來的目標檢測模型演示
      • 訓練出來的分類模型演示【固定框檢測模式】
    • 一鍵訓練YOLOv3 YOLOv4 YOLOv5 方法
      • 轉換數據
      • 訓練與檢測
      • 訓練
      • 檢測
    • 核心部分介紹
      • 目標檢測數據標注
      • 分類
      • 分類訓練部分
      • 分類推理部分
    • 模型導出部分
    • 后續優化
      • 優化tips1: 使用更多數據增強
      • 優化tips2:使用高質量相機采集,或者修改圖片size獲取更高清圖片
      • 優化tips3:使用更高質量跟蹤算法:比如deepsort ,我已經做了,后續慢慢會開源
      • 優化tips4:在使用的使用,盡量使用左右上下平移,這樣會保證boxes更擬合。然后在調整了前后距離(大小)后,重新描框。
      • 優化tips5:使用更接近場景的mix_up 圖片。
      • 優化tips6:更換更多場景,更多人物攝制,并獲取更多數據。
      • 優化tips7:大家一起加入進來一起完善!我有個優質公眾號和兩個深度學習交流群~大家進來一起交流,獲取大量AI 深度學習數據集,和交流更優質的算法
    • 總結

數據標注費時費力,又費錢!深諳其苦的我開發了這個項目。

大家好,我是大家的好朋友~ cv調包俠,深度學習算法攻城獅(實習僧)一枚, 下面我將誠心地發布一個自己的原創:Auto_maker!

他能干什么?

大家可以特別方便地通過我的Auto_maker 實現目標檢測數據集的實時制作,包括:10分鐘完成 真實數據采集,自動標注,轉換,增強,并且可以直接進行yolov3, yolov4 ,yolov5,efficientdet等,并且可以直接導出成onnx,并使用openvino和tensorRT加速;除了檢測以外,還支持分類算法,可以一分鐘完成圖片智能分類歡迎star~

同時他具有高精度,高實時性,高效率,他是人工標注的10倍以上效率,并且精度可控~

cv調包俠錄制了一個視頻講解~大家也可以通過這篇文章得到更多的了解!

也可以查看GIF 動圖

github:https://github.com/CVUsers/Auto_maker

項目比較簡單,cv調包俠不到半小時就把代碼下面開始講解項目結構和代碼。剩下的就是在完善邏輯,優化用戶體驗了,改了許久,大家可以方便地使用,簡單地體驗~

項目結構與使用教程

目標檢測模式

在我們運行Auto_maker 前,需要安裝opencv的庫:opencv-contrib-python 庫

pip install opencv-contrib-python

然后運行 get_images.py 就能看到實時圖像,再按下"s"鍵就可以用鼠標繪制目標框,繪制完后回車一下~

然后按下“b”鍵就會看到控制臺輸出開始保存的提示~

然后我們可以左右上下地平移物體,如果內外前后地移動了物體后,追蹤框若是發生了偏移,那么就請再按一下“s”重新標注一下~會繼續保存圖片到images文件夾中,同時也會生成xml到Annotations文件夾中。

就這樣,大家不斷地平移,切換場景,并且打開mix_up模式,會獲得更多,更豐富的圖片,這樣對我們訓練的模型也會更準確和更泛化。

尤其是目標檢測中的多尺度問題,需要我們豐富數據及其標注框在圖片中的相對大小來解決,我在mix_up 的同時使用了隨機等比例縮放,獲得更多樣式~。

parser = argparse.ArgumentParser('Auto_maker')parser.add_argument('-t', "--tracker", type=str, default='csrt', help='choose opencv tracker methods')parser.add_argument('-i', '--source_path', type=str, default='0',help='0 or 1 is your capture, or use video.mp4 or use path_dir like: ./images/')parser.add_argument('--show', default=True, action='store_true', help='mix_up picture show')parser.add_argument('--mix', default='./mix_img/', action='store_true',help='default:False is do not use mix_up method, and use ./mix_up to mix_up')parser.add_argument('--Scaling_probability', default=[0.6, 1.4], action='store_true',help='The probability of scaling your boxes')classes_list = ['UsingPhone', 'LikePhone'] # 類別名稱 不建議使用"_"命名obj_name = classes_list[0] # 此次標注的類別名稱args = parser.parse_args()counter, flag = 0, 0path = "images/"test_path = 'test_img/'OPENCV_OBJECT_TRACKERS = { # OPENCV_OBJECT_TRACKERS 默認使用csrt"csrt": cv2.TrackerCSRT_create,"kcf": cv2.TrackerKCF_create,"boosting": cv2.TrackerBoosting_create,"mil": cv2.TrackerMIL_create,"tld": cv2.TrackerKCF_create,"medianflow": cv2.TrackerMedianFlow_create,"mosse": cv2.TrackerMOSSE_create}

像這樣,tracker可以切換成:csrt, kcf,boosting等方式,這是opencv中的追蹤算法,csrt是較準的,同時你也可以使用deepsort 進行跟蹤,或者使用自己訓練好的一個模型,進行其他大量數據的預訓練。

–source_path 我們可以切換為0 :使用內置相機,切換為1:使用外界相機; 切換為圖片路徑:images/ 下的圖片,可以這樣標注~,切換為視頻路徑:demo.mp4 來標注視頻幀,注意一個視頻幀數很多,大家可以修改程序中的cv2.waitkey()來改善。

–show 就是顯示我們的mix_up 的圖片~

–mix 是我們使用mix_up 并且使用隨機等比例縮放的路徑,如果default = False,就是不使用mix_up 做增強,如果使用,就將mix_img 的路徑放入:./mix_img/

–Scaling_probability 就是縮放比例的區間。

classes_list :我們將所有的類別寫進來,并

影像分類模式

影像分類中,使用簡單的固定ROI方式,在運行maker_classification.py 后我們可以輕松地按下s鍵保存圖片~圖片就會根據main中的類別名稱保存到data/train/類別名/下面,如果是測試集,就在main中修改為test模式

parser.add_argument('--dtype', type=str, default='pause', help='your label') parser.add_argument('--train_test', type=str, default='test', help='train/test')

–dtype:類別 修改成自己的數據類別,就會在data/train/下面生成這樣的文件夾,里面存放這個類別的圖片

–train_test 現在收集的數據是訓練集還是測試集。切換train或者test會分別保存到train或者test文件夾下。

數據采集演示與訓練出來的模型演示

GIF和演示視頻公眾號文章查看

github:https://github.com/CVUsers/Auto_maker
公眾號獲取4w玩手機數據回復:玩手機:

github圖片

數據標注部分

自動標注過程

數據效果圖


標注文件集

訓練出來的目標檢測模型演示

模型已放在github上,輕量級模型,這兩天會更新更高精度模型~

訓練出來的分類模型演示【固定框檢測模式】

模型已放在github上,輕量級模型,這兩天會更新更高精度模型~

一鍵訓練YOLOv3 YOLOv4 YOLOv5 方法

轉換數據

我們現在得到了所有的圖片/標注文件(同名),那么就可以開始訓練了,訓練過程很簡單,我們只需要轉換一下數據:

運行voc_label.py 數據轉換成YOLO格式:通過這個腳本,你可以在labels文件夾中生成歸一化后的標簽,同時生成一份訓練集:train.txt 和測試集test.txt

我們只需要修改classes:類別即可。

import os import xml.etree.ElementTree as ET import os import cv2 import random classes = ['UsingPhone', 'LikePhone']def convert(size, box):print(size, box)dw = 1. / size[0]dh = 1. / size[1]x = (box[0] + box[1]) / 2.0y = (box[2] + box[3]) / 2.0w = box[1] - box[0]h = box[3] - box[2]x = x * dww = w * dwy = y * dhh = h * dhreturn (x, y, w, h)def convert_annotation(image_id):print(image_id)in_file = open(r'./Annotations/%s' % (image_id), 'rb') # 讀取xml文件路徑out_file = open('./labels/%s.txt' % (image_id.split('.')[0]), 'w') # 需要保存的txt格式文件路徑tree = ET.parse(in_file)root = tree.getroot()size = root.find('size')w = int(size.find('width').text)h = int(size.find('height').text)if w == 0 and h == 0:img = cv2.imread('./images/' +image_id.replace('xml', 'jpg'))w, h = img.shape[1], img.shape[0]for obj in root.iter('object'):cls = obj.find('name').textif cls not in classes:print('*******************************'*2, cls)breakcls_id = classes.index(cls)xmlbox = obj.find('bndbox')b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),float(xmlbox.find('ymax').text))bb = convert((w, h), b)out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')image_ids_train = os.listdir('./Annotations') # 讀取xml文件名索引for image_id in image_ids_train:print(image_id)convert_annotation(image_id)trainval_percent = 0.1 # 可自行進行調節 train_percent = 1 xmlfilepath = './labels' total_xml = os.listdir(xmlfilepath) num = len(total_xml) list = range(num) tv = int(num * trainval_percent) tr = int(tv * train_percent) trainval = random.sample(list, tv) train = random.sample(trainval, tr) ftest = open('./test.txt', 'w') ftrain = open('./train.txt', 'w')for i in list:name = total_xml[i] + '\n'if i in trainval:if i in train:ftest.write('../images/' + name.replace('txt', 'jpg'))else:ftrain.write('../images/' + name.replace('txt', 'jpg')) ftrain.close() ftest.close()

訓練與檢測

訓練

下面YOLOv5 和V3 一樣,我們只需要修改yolov5/data/voc.yaml即可(v3 和v4 若是使用darknet也是差不多哦~):

train: ../train.txt # 16551 images val: ../test.txt # 4952 images# number of classes nc: 2# class names names: ['UsingPhone', 'LikePhone']

在訓練此yolov5 前,請安裝pytorch1.6 以上,如果你使用的是torch1.5以下,那么請區clone yolov5 的第二個版本以下,或者你使用yolov3~

然后修改train.py 的這個部分

parser = argparse.ArgumentParser()parser.add_argument('--weights', type=str, default='weights/yolov5s.pt', help='initial weights path')parser.add_argument('--cfg', type=str, default='models/yolov5s.yaml', help='model.yaml path')parser.add_argument('--data', type=str, default='data/voc.yaml', help='data.yaml path')parser.add_argument('--hyp', type=str, default='data/hyp.scratch.yaml', help='hyperparameters path')parser.add_argument('--epochs', type=int, default=300)parser.add_argument('--batch-size', type=int, default=10, help='total batch size for all GPUs')parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, test] image sizes')parser.add_argument('--rect', action='store_true', help='rectangular training')parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')parser.add_argument('--notest', action='store_true', help='only test final epoch')parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')parser.add_argument('--device', default='0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer')parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')parser.add_argument('--log-imgs', type=int, default=16, help='number of images for W&B logging, max 100')parser.add_argument('--log-artifacts', action='store_true', help='log artifacts, i.e. final trained model')parser.add_argument('--workers', type=int, default=0, help='maximum number of dataloader workers')parser.add_argument('--project', default='runs/train', help='save to project/name')parser.add_argument('--name', default='exp', help='save to project/name')parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')opt = parser.parse_args()

–weights 預訓練模型路徑

–cfg 網絡結構路徑

–data voc.yaml路徑

訓練效果圖可以在runs 下查看result.txt

或者使用tensorboard查看,我們到yolov5或者3 路徑下執行tensorboard --logdir=runs

mAP和precision 和recall 如下,我的模型只訓練了70次~ 我接下來會使用4w張圖片訓練完,來查看準確率,并且實際體驗效果,然后我會放在我的github和公眾號:70次效果也不錯~

這是損失~

檢測

parser = argparse.ArgumentParser()parser.add_argument('--weights', nargs='+', type=str, default=r'D:\cvuser\Auto_maker\yolov5\runs\train\exp7\weights\best.pt', help='model.pt path(s)')parser.add_argument('--source', type=str, default='0', help='source') # file/folder, 0 for webcamparser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')parser.add_argument('--conf-thres', type=float, default=0.45, help='object confidence threshold')parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')parser.add_argument('--device', default='0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')parser.add_argument('--view-img', action='store_true', help='display results')parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')parser.add_argument('--augment', action='store_true', default=True, help='augmented inference')parser.add_argument('--update', action='store_true', help='update all models')parser.add_argument('--project', default='runs/detect', help='save results to project/name')parser.add_argument('--name', default='exp', help='save results to project/name')parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')opt = parser.parse_args()

我們把–weights 改成模型的絕對路徑即可~

并且打開–augment 為True

核心部分介紹

目標檢測數據標注

在目標檢測數據標注代碼:get_images.py中:

parser = argparse.ArgumentParser('Auto_maker')parser.add_argument('-t', "--tracker", type=str, default='csrt', help='choose opencv tracker methods')parser.add_argument('-i', '--source_path', type=str, default='0',help='0 or 1 is your capture, or use video.mp4 or use path_dir like: ./images/')parser.add_argument('--show', default=True, action='store_true', help='mix_up picture show')parser.add_argument('--mix', default='./mix_img/', action='store_true',help='default:False is do not use mix_up method, and use ./mix_up to mix_up')parser.add_argument('--Scaling_probability', default=[0.6, 1.4], action='store_true',help='The probability of scaling your boxes')classes_list = ['UsingPhone', 'LikePhone'] # 類別名稱 不建議使用"_"命名obj_name = classes_list[0] # 此次標注的類別名稱args = parser.parse_args()counter, flag = 0, 0path = "images/"test_path = 'test_img/'OPENCV_OBJECT_TRACKERS = { # OPENCV_OBJECT_TRACKERS 默認使用csrt"csrt": cv2.TrackerCSRT_create,"kcf": cv2.TrackerKCF_create,"boosting": cv2.TrackerBoosting_create,"mil": cv2.TrackerMIL_create,"tld": cv2.TrackerKCF_create,"medianflow": cv2.TrackerMedianFlow_create,"mosse": cv2.TrackerMOSSE_create}if os.path.isdir(args.source_path): # 圖片文件夾自動標注run_on_images(args.source_path, mix=args.mix)elif os.path.isfile(args.source_path): # 標注一個視頻文件run_on_video(args.source_path, mix=args.mix)elif '0' in args.source_path or '1' in args.source_path: # 實時標注 (建議使用實際使用時的相機錄制~)run_on_video(int(args.source_path), mix=args.mix)

程序入口,判斷傳入的是文件夾還是視頻還是相機路徑,做出相應響應。

run_on_video 函數

saveimg = Falsemix_img = Falsewd = getcwd()tracker = OPENCV_OBJECT_TRACKERS[args.tracker]() # 定義追蹤器intBB = Nonevs = cv2.VideoCapture(source)while True:frame = vs.read()frame = frame[1]frame = cv2.resize(frame, (640, 480))frame = cv2.flip(frame, 3)frame1 = frame.copy()(H, W) = frame.shape[:2]if frame is None:breakif intBB is not None:(success, box) = tracker.update(frame)if success:(x, y, w, h) = [int(v) for v in box]if mix:Scaling_probability = random.randint(args.Scaling_probability[0] * 10,args.Scaling_probability[1] * 10) / 10try:mix_frame = cv2.resize(frame1[y:y + h, x:x + w],(int(w * Scaling_probability), int(h * Scaling_probability)))w_, h_ = int(w * Scaling_probability), int(h * Scaling_probability)mix_img = mix_roi_img(mix, mix_frame, x, y, w_, h_)if saveimg:saveROIImg(frame, frame1, x, y, x + w_, y + h_, obj_name, flag=True, mix=mix_img)except:passcv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)if saveimg:saveROIImg(frame, frame1, x, y, x + w, y + h, obj_name)cv2.imshow('frame', frame)key = cv2.waitKey(50) & 0xFFif key == ord('s'):print('class is:', obj_name)tracker = OPENCV_OBJECT_TRACKERS[args.tracker]()tracker1 = OPENCV_OBJECT_TRACKERS[args.tracker]()intBB = NoneintBB = cv2.selectROI('frame', frame, fromCenter=False, showCrosshair=True)tracker.init(frame, intBB)elif key == ord('b'):saveimg = Trueelif key == 27:cv2.destroyAllWindows()vs.release()break

先通過tracker = args.tracker () 定義追蹤器,然后顯示實時視頻,監聽鼠標,若為“s” 那么啟動追蹤器,并獲取關鍵區域roi。同時初始化追蹤器。然后獲取roi的bounding box位置

intBB = cv2.selectROI('frame', frame, fromCenter=False, showCrosshair=True)tracker.init(frame, intBB)

在mix_up 中:

def mix_roi_img(mix, img, x, y, w, h): # 使用mix_up貼圖global counterif os.path.isdir(mix):i = random.choice(os.listdir(mix))img_back = cv2.imread(os.path.join(mix, i))try:img_back = cv2.resize(img_back, (640, 480))except:print(f'{os.path.join(mix, i)} connot open it!')rows, cols, channels = img.shape # rows,cols最后一定要是前景圖片的,后面遍歷圖片需要用到center = [x, y] # 在新背景圖片中的位置for i in range(cols):for j in range(rows):# if dilate[i, j] == 0:if center[0] + i < 640 and center[1] + j < 480:img_back[center[1] + j, center[0] + i] = img[j, i] # 此處替換顏色,為BGR通道cv2.imshow(f'mix_{i}', img_back)cv2.waitKey(30)counter += 1if counter % 20 == 0:cv2.destroyAllWindows()return img_back

默認640x480 的圖片大小,大家可以修改,然后把圖片進行隨機貼合。大家可以在這里做更多的貼圖算法優化,我這里就是像素點的轉換,還有其他方法,比如邊緣檢測,將需要的部分留下,不需要的部分用原mix_up 的圖片替換~

# 定義一個創建一級分支object的函數 def create_object(root, xi, yi, xa, ya, obj_name): # 參數依次,樹根,xmin,ymin,xmax,ymax_object = ET.SubElement(root, 'object') # 創建一級分支objectname = ET.SubElement(_object, 'name') # 創建二級分支name.text = str(obj_name)pose = ET.SubElement(_object, 'pose')pose.text = 'Unspecified'truncated = ET.SubElement(_object, 'truncated')truncated.text = '0'difficult = ET.SubElement(_object, 'difficult')difficult.text = '0'bndbox = ET.SubElement(_object, 'bndbox') # 創建bndboxxmin = ET.SubElement(bndbox, 'xmin')xmin.text = '%s' % xiymin = ET.SubElement(bndbox, 'ymin')ymin.text = '%s' % yixmax = ET.SubElement(bndbox, 'xmax')xmax.text = '%s' % xaymax = ET.SubElement(bndbox, 'ymax')ymax.text = '%s' % ya# 創建xml文件的函數 def create_tree(image_name, imgdir, h, w):global annotationannotation = ET.Element('annotation') # 創建樹根annotationfolder = ET.SubElement(annotation, 'folder') # 創建一級分支folderfolder.text = (imgdir) # 添加folder標簽內容filename = ET.SubElement(annotation, 'filename') # 創建一級分支filenamefilename.text = image_namepath = ET.SubElement(annotation, 'path') # 創建一級分支pathpath.text = getcwd() + '\{}\{}'.format(imgdir, image_name) # 用于返回當前工作目錄source = ET.SubElement(annotation, 'source') # 創建一級分支sourcedatabase = ET.SubElement(source, 'database') # 創建source下的二級分支databasedatabase.text = 'Unknown'size = ET.SubElement(annotation, 'size') # 創建一級分支sizewidth = ET.SubElement(size, 'width') # 創建size下的二級分支圖像的寬、高及depthwidth.text = str(w)height = ET.SubElement(size, 'height')height.text = str(h)depth = ET.SubElement(size, 'depth')depth.text = '3'segmented = ET.SubElement(annotation, 'segmented') # 創建一級分支segmentedsegmented.text = '0'

這邊是xml樹的構建。

def saveROIImg(frame, img, xmin, ymin, xmax, ymax, obj_name, flag=False, mix=False): # 保存圖片和xmlglobal counter, saveimgname = find_max_name(obj_name, mix)H, W = frame.shape[0], frame.shape[-2]name += 1if flag:print("Saving image:", name, xmin, ymin, xmax, ymax)cv2.imwrite(path + f'mix_{obj_name}_' + str(name) + ".jpg", mix)cv2.rectangle(mix, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2)cv2.imwrite(test_path + f'mix_{obj_name}_' + str(name) + ".jpg", mix)create_tree(f'mix_{obj_name}_' + str(name) + '.jpg ', 'images', H, W)create_object(annotation, xmin, ymin, xmax, ymax, obj_name)cv2.waitKey(180)tree = ET.ElementTree(annotation)tree.write('.\Annotations\{}.xml'.format(f'mix_{obj_name}_' + str(name)))returnprint("Saving image:", name, xmin, ymin, xmax, ymax)cv2.imwrite(path + f'{obj_name}_' + str(name) + ".jpg", img)cv2.imwrite(test_path + f'{obj_name}_' + str(name) + ".jpg", frame)cv2.imshow('images', img)create_tree(f'{obj_name}_' + str(name) + '.jpg ', 'images', H, W)create_object(annotation, xmin, ymin, xmax, ymax, obj_name)cv2.waitKey(50)tree = ET.ElementTree(annotation)tree.write('.\Annotations\{}.xml'.format(f'{obj_name}_' + str(name)))

保存模式,選擇是否保存mix_up的圖片。

分類

maker_classification.py

import argparseimport win32api import win32con import cv2 as cv import os import numpy as np save_path = 'data'def get_roi(frame, x1, x2, y1, y2):dst = frame[y1+2:y2, x1+2:x2]cv.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), thickness=1)return dstdef get_data(dtype):max = 0for i in os.listdir('data/{}/{}'.format(args.train_test, dtype)):if int(i.split('_')[2].split('.')[0]) > max:max = int(i.split('_')[2].split('.')[0])return max + 1def main():if not os.path.isdir('./data/'):os.makedirs('./data/')if not os.path.isdir('./data/train/'):os.makedirs('./data/train/')if not os.path.isdir('./data/test/'):os.makedirs('./data/test/')if not os.path.isdir('./data/train/{}'.format(args.dtype)):os.makedirs('./data/train/{}'.format(args.dtype))if not os.path.isdir('./data/test/{}'.format(args.dtype)):os.makedirs('./data/test/{}'.format(args.dtype))m_0 = get_data(args.dtype)capture = cv.VideoCapture(0)while True:ret, frame = capture.read()roi = get_roi(frame, 100, 350, 100, 350)k = cv.waitKey(20)if k == 27: # 按下ESC退出breakelif k == ord('s'): # 按下'A'會保存當前圖片到指定目錄下cv.imwrite("{}/{}/{}/{}.jpg".format(save_path, args.train_test, args.dtype, m_0), roi)m_0 += 1# flip_image = cv.flip(skin, 1) # 這里用到的是水平翻轉,因為后面的參數是一# cv.imwrite("E:\\aiFile\\picture\\gesture_data\\0\\%s.jpg" % m_0, flip_image)# m_0 += 1print('正在保存0-roi圖片,本次圖片數量:', m_0)cv.imshow("roi", roi)cv.imshow("frame", frame)c = cv.waitKey(20)if c == 27:breakcv.waitKey(0)capture.release()cv.destroyAllWindows()if __name__ == '__main__':parser = argparse.ArgumentParser()parser.add_argument('--dtype', type=str, default='pause', help='your label')parser.add_argument('--train_test', type=str, default='test', help='train/test')args = parser.parse_args()main()

maker_by_Guss.py

import cv2 import imutils import numpy as np import argparse import osbg = Nonedef run_avg(image, aWeight):global bgif bg is None:bg = image.copy().astype('float')returncv2.accumulateWeighted(image, bg, aWeight)def segment(image, threshold=25):global bgdiff = cv2.absdiff(bg.astype('uint8'), image)thresholded = cv2.threshold(diff,threshold,255,cv2.THRESH_BINARY)[1](cnts, _) = cv2.findContours(thresholded.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)if len(cnts) == 0:returnelse:segmented = max(cnts, key=cv2.contourArea)return (thresholded, segmented)def main(dtype):aWeight = 0.5camera = cv2.VideoCapture(0)top, right, bottom, left = 90, 380, 285, 590num_frames = 0thresholded = Nonecount = 0while(True):(grabbed, frame) = camera.read()if grabbed:frame = imutils.resize(frame, width=700)frame = cv2.flip(frame, 1)clone = frame.copy()(height, width) = frame.shape[:2]roi = frame[top:bottom, right:left]gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)gray = cv2.GaussianBlur(gray, (7, 7), 0)if num_frames < 30:run_avg(gray, aWeight)else:hand = segment(gray)if hand is not None:(thresholded, segmented) = handcv2.drawContours(clone, [segmented + (right, top)], -1, (0, 0, 255))cv2.rectangle(clone, (left, top), (right, bottom), (0, 255, 0), 2)num_frames += 1cv2.imshow('Video Feed', clone)if not thresholded is None:cv2.imshow('Thesholded', thresholded)keypress = cv2.waitKey(1) & 0xFFif keypress == ord('q'):breakif keypress == ord('s'):if not os.path.isdir('./data/'):os.makedirs('./data/')if not os.path.isdir('./data/train/'):os.makedirs('./data/train/')if not os.path.isdir('./data/test/'):os.makedirs('./data/test/')if not os.path.isdir('./data/train/{}'.format(args.dtype)):os.makedirs('./data/train/{}'.format(args.dtype))if not os.path.isdir('./data/test/{}'.format(args.dtype)):os.makedirs('./data/test/{}'.format(args.dtype))cv2.imwrite('data/{}/saved_v2_{:04}.jpg'.format(dtype, count), thresholded)count += 1print(count, 'saved.')else:camera.release()breakif __name__ == '__main__':parser = argparse.ArgumentParser()parser.add_argument('--dtype', type=str, default='pause', help='your label')args = parser.parse_args()main(args.dtype)cv2.destroyAllWindows()

使用高斯邊緣消除后保存,適用于特征鮮明物體。

分類訓練部分

class Net(nn.Module):def __init__(self, num_classes=2):super().__init__()self.net = mobilenet_v2(pretrained=True) # backbone + neck + headself.avg_pool = nn.AdaptiveAvgPool2d(1)self.logit = nn.Linear(1280, len(args.classes)) # [bs, 1280] -> [bs, classes]def forward(self, x): # [bs,3,224,224]x = self.net.features(x) # [bs, 1280, 7, 7] 224//32x = self.avg_pool(x) # [bs, 1280, 1, 1]x = x.view(x.size(0), -1) # [bs, 1280]# x = torch.reshape()x = self.logit(x)return x

定義網絡和主干網絡

def run(images_list, val_list):train_dataset = GestureDataset(images_list)train_dataloader = DataLoader(train_dataset,batch_size=args.batch_size,shuffle=True,num_workers=0,)val_dataset = GestureDataset(val_list)val_dataloader = DataLoader(val_dataset,batch_size=args.batch_size,shuffle=True,num_workers=0)model = Net()model.cuda()optimizer = torch.optim.Adam(model.parameters(), lr=1.5e-4)loss_fn = nn.CrossEntropyLoss()best_score = float("inf") # 0XFFFFFFFbest_acc = 0.for epoch in range(args.epochs):print('Epoch:', epoch)train_one(train_dataloader, model, optimizer, loss_fn, None)scores = val_one(val_dataloader, model, loss_fn)if scores['loss'] <= best_score:best_score = scores['loss']print('*****best_loss:', best_score, 'acc:', best_acc)if scores['accuracy'] >= best_acc:best_acc = scores['accuracy']print('*******save best*******', epoch)torch.save(model.state_dict(), "ckpt/model.pth")

訓練部分

class GestureDataset(Dataset):def __init__(self, images_list, transformers=None):self.images_list = images_list # 3000self.transformers = transformersdef __len__(self):return len(self.images_list)def normalize(self, image):image = np.transpose(image, (2, 0, 1)) # [3,224,224]mean = [0.485, 0.56, 0.06]std = [0.229, 0.224, 0.225]image = image.astype(np.float32) / 255 # [0,1]image -= np.array(mean).reshape((3,1,1))image /= np.array(std).reshape((3,1,1))# image[0] -= mean # [-0.5, 0.5]# image /=std # []return imagedef __getitem__(self, index: int):image_size = 224name:str = self.images_list[index]image_name = nameimage = np.array(Image.open(image_name)) # uint8 [0-255]image = cv2.resize(image, (image_size,image_size))label_str = args.classes.index(name.split("\\")[-2])label = int(label_str)result = {"image": self.normalize(image),"label": label}return result

數據增強和數據讀取

分類推理部分

detect.py

import argparseimport torch import cv2 import os from PIL import Image from torchvision import transforms import torch, torch.nn as nn, torch.nn.functional as F from torchvision.models.mobilenet import mobilenet_v2 import time device = torch.device('cuda') mean = [0.485, 0.456, 0.406] std = [0.229, 0.224, 0.225] transform = transforms.Compose([transforms.Resize(224),transforms.ToTensor(),transforms.Normalize(mean=mean,std=std) ])class Net(nn.Module):def __init__(self, num_classes=2):super().__init__()self.net = mobilenet_v2(pretrained=True) # backbone + neck + headself.avg_pool = nn.AdaptiveAvgPool2d(1)self.logit = nn.Linear(1280, len(args.classes)) # [bs, 1280] -> [bs, classes]def forward(self, x): # [bs,3,224,224]x = self.net.features(x) # [bs, 1280, 7, 7] 224//32x = self.avg_pool(x) # [bs, 1280, 1, 1]x = x.view(x.size(0), -1) # [bs, 1280]# x = torch.reshape()x = self.logit(x)return x def predict():# net = torch.load('./ckpt/model.pth')# net = net.cuda()net = Net()net.load_state_dict(torch.load(args.model))net = net.cuda()net.eval()# net.to("cuda")# net.to(torch.device("cuda:0"))torch.no_grad()return netdef run(img):img = Image.fromarray(img[:, :, ::-1])# img = Image.open(img_path)img = transform(img).unsqueeze(0)img_ = img.to(device)outputs = net(img_)outputs = torch.softmax(outputs, dim=1)score, predicted = torch.max(outputs, 1)return score[0].item(), predicted[0].item()def get_roi(frame, x1, x2, y1, y2):dst = frame[y1:y2, x1:x2]cv2.rectangle(frame, (x1 -2, y1-2), (x2+4, y2+4), (0, 0, 255), thickness=2)return dstif __name__ == '__main__':parser = argparse.ArgumentParser()parser.add_argument('--classes', type=str, default=['pause', 'hand'], help='your label')parser.add_argument('--source', type=int, default=0, help='your label')parser.add_argument('--model', type=str, default='./ckpt/model.pth', help='your label')parser.add_argument('--threshold', type=str, default='0.9', help='your label')args = parser.parse_args()net = predict()video = cv2.VideoCapture(args.source)while True:time1 = time.time()ret, img = video.read()img_copy = imgroi = get_roi(img, 100, 324, 100, 324)# cv2.rectangle(img_copy, (95, 95), (328, 328), (0, 0, 255), thickness=1)if ret:cv2.imshow('img', roi)score, name = run(roi)name = args.classes[name]if float(score) >= float(args.threshold):cv2.putText(img_copy, str(name + ' '+str(round(score, 2))), (90, 90), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255, 255, 255), 2)cv2.imshow('frame', img_copy)time2 = time.time()print("Inference Time:", round(time2 - time1, 3))cv2.waitKey(5)

效果圖:

模型導出部分

mobilenet-v2 模型導出onnx部分

import torch,onnx,collections import torch.nn as nn from torchvision.models.mobilenet import mobilenet_v2 class Net(nn.Module):def __init__(self, num_classes=3):super().__init__()self.net = mobilenet_v2(pretrained=True) # backbone + neck + headself.avg_pool = nn.AdaptiveAvgPool2d(1)self.logit = nn.Linear(1280, num_classes) # [bs, 1280] -> [bs, classes]def forward(self, x): # [bs,3,224,224]x = self.net.features(x) # [bs, 1280, 7, 7] 224//32x = self.avg_pool(x) # [bs, 1280, 1, 1]x = x.view(x.size(0), -1) # [bs, 1280]# x = torch.reshape()x = self.logit(x)return xprint('notice !!!! ----> use python3 run this script!!! \n') INPUT_DICT = 'ckpt\model.pth' OUT_ONNX = 'ckpt\cls_model.onnx'x = torch.randn(1, 3, 224, 224) input_names = ["input"] out_names = ["output"] net = Net() xmodel= torch.load(INPUT_DICT, map_location=torch.device('cuda')) net.load_state_dict(xmodel) net.eval()torch.onnx.export(net, x, OUT_ONNX, export_params=True, training=False, input_names=input_names, output_names=out_names) print('please run: python3 -m onnxsim test.onnx test_sim.onnx\n') print('convert done!\n')

yolov3 和yolov5 導出和openvino推理代碼見:
https://mp.weixin.qq.com/s/m-bn-Q0dhfav-YsI5b-oLg

yolov5 使用TensorRT推理代碼見:
https://mp.weixin.qq.com/s/V6jwK14gcyIqXl-z8ed-7Q

后續優化

這是cv調包俠的原創項目,沒有參考過任何人,當然后來也看過網上用類似的方法實現了,但是我的更完整,完善,可移植性高,并且自帶數據增強。

數據增強中,massic我沒有使用,因為大多數模型自帶massic,如果經過兩次massic會更小,誤檢上會有問題。

優化tips1: 使用更多數據增強

但是不是越多增強越好~,比如我們沒有使用翻轉,因為模型自帶翻轉,以及hsv通道的增強,旋轉和亮度,我們無需管,還有其他的數據正確策略,比如cut-mix ,等等,歡迎fork我的項目,并且完善~讓項目更加簡單,更高效:

github:https://github.com/CVUsers/Auto_maker

優化tips2:使用高質量相機采集,或者修改圖片size獲取更高清圖片

優化tips3:使用更高質量跟蹤算法:比如deepsort ,我已經做了,后續慢慢會開源

優化tips4:在使用的使用,盡量使用左右上下平移,這樣會保證boxes更擬合。然后在調整了前后距離(大小)后,重新描框。

優化tips5:使用更接近場景的mix_up 圖片。

優化tips6:更換更多場景,更多人物攝制,并獲取更多數據。

優化tips7:大家一起加入進來一起完善!我有個優質公眾號和兩個深度學習交流群~大家進來一起交流,獲取大量AI 深度學習數據集,和交流更優質的算法

總結

項目已發布:github:https://github.com/CVUsers/Auto_maker

附帶4w張玩手機數據:關注公眾號回復玩手機

公眾號:DeepAi 視界

二維碼:


我們還可以標注什么數據?

答:絕大多數voc,coco數據,例如:貓狗,人,車,各種物體,但是過小的物體慎用~

作者 : 周小夏 cv調包俠 本科大三 深度學習算法攻城獅實習僧 上海第二工業大學

總結

以上是生活随笔為你收集整理的【效率提高10倍项目原创发布!】深度学习数据自动标注器开源 目标检测和图像分类(高精度高效率)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。