【工業機器視覺】基于深度學習的儀表盤識讀(2)-CSDN博客
數據標注
標注擴展
Labelme 和 LabelImg 都是用于創建機器學習和計算機視覺項目所需標注數據的工具。它們都允許用戶通過圖形界面手動標注圖像,但各自有其特點和適用場景。
Labelme
- 開發語言:Python
- 標注類型:Labelme 支持多種標注類型,包括但不限于多邊形(polygon)、矩形(rectangle)、線段(line)、點(point)等。它非常適合需要精確標注物體邊界的情況,比如在醫療影像、自動駕駛等領域。
- 文件格式:標注結果通常保存為 JSON 文件,其中包含每個標注對象的坐標信息、標簽名稱等。
- 靈活性:Labelme 提供了插件系統,可以擴展其功能,如支持更多的圖像格式或添加自定義的標注類型。
- 跨平臺:基于 Python 的 Qt 庫構建,可以在 Windows、macOS 和 Linux 上運行。
LabelImg
- 開發語言:Python
- 標注類型:主要支持矩形框(bounding box)標注,適合于目標檢測任務。對于需要簡單快速地對多個對象進行框選標注的任務來說非常方便。
- 文件格式:標注結果可以保存為 Pascal VOC XML 格式或者 YOLO txt 格式,這兩種格式都是目標檢測任務中常用的標注文件格式。
- 輕量化:相比于 Labelme,LabelImg 更加輕量化,易于安裝和使用,尤其適合初學者。
- 跨平臺:同樣基于 Python 的 Qt 庫,可以在不同操作系統上運行。
安裝擴展包:
pip install labelme labelimg
指針區域
使用labelimg進行BOX標注,標簽類別:{'area_p', 'p0', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9'},p0~p9為最低位x0.001標簽類別,area_p為其他高位指針區域標簽類別。
示例:
標簽文件是VOC XML文件數據,后面再通過腳本轉換成YOLO格式數據。
梅花針分割
使用labelme進行實例切割,標簽類別:{'pointer', 'circle_area'},pointer是紅色梅花針區域,circel_area是刻度圓。
示例:
標簽文件是JSON格式文件數據,后面再通過腳本轉換成YOLO格式數據。
字輪區域
使用labelimg進行BOX標注,標簽類別:{'d0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', ?'d10', 'd11', 'd12', 'd13', 'd14', 'd15', 'd16', 'd17', 'd18', 'd19'},d0~9表示數字剛好在字輪窗口中心位置,也就是非過渡狀態,d10~19表示各個數字的過渡狀態。
示例:
標簽文件是VOC XML文件數據,后面再通過腳本轉換成YOLO格式數據。
數據劃分
在深度學習中,訓練集和驗證集是用于模型開發過程中的兩個重要數據集。它們各自扮演著不同的角色,確保最終模型的性能和泛化能力。
訓練集(Training Set)
- 用途:訓練集主要用于訓練模型。即,在這個數據集上調整模型的權重或參數,使模型能夠學習到數據中的特征和模式。
- 特點:通常包含大量帶標簽的數據樣本,這些數據樣本應該盡可能地代表實際應用環境中的數據分布。
- 影響:如果訓練集的質量不高(如數據量不足、標注不準確或偏差),可能會導致模型過擬合或欠擬合,從而影響其性能。
驗證集(Validation Set)
- 用途:驗證集用于在訓練過程中評估模型的表現,幫助選擇模型的最佳配置(例如,超參數調優)。它提供了一個獨立于訓練集的反饋機制,用以監測模型是否開始過擬合訓練數據。
- 特點:與訓練集類似,驗證集也應該是有代表性的,并且它的標簽也是已知的。但是,它不應該被用來直接更新模型參數;相反,它是用來決定何時停止訓練(早停法)或選擇最佳模型架構/超參數。
- 影響:通過驗證集可以有效防止過擬合,確保模型不僅在訓練數據上表現良好,而且在未見過的數據上也能保持良好的性能。
注意事項
- 劃分比例:常見的是將數據劃分為70%訓練集和30%驗證集,或者采用交叉驗證的方法來更充分利用有限的數據。具體的比例可以根據實際情況調整。
- 測試集:除了訓練集和驗證集之外,有時還會有一個測試集(Test Set),用于最終評估模型的真實性能。測試集在整個訓練和驗證過程中都應該保持完全獨立,直到最后評估時才使用。
正確管理和使用訓練集和驗證集對于構建一個有效的深度學習模型至關重要。這有助于確保模型不僅能很好地適應訓練數據,還能對新數據做出準確預測。
數據劃分代碼:
import argparse
import os
import random
from os import getcwd# root = getcwd() + '\\my_datas\\pointer-seg\\'
root = getcwd() + '\\my_datas\\detect-pointer\\'
# root = getcwd() + '\\my_datas\\detect-digit\\'
parser = argparse.ArgumentParser()parser.add_argument('--img_path', default='IMAGES', type=str,help='input images file path')
parser.add_argument('--txt_path', default='TXT_LABELS', type=str,help='output txt label path')
opt = parser.parse_args()trainval_percent = 1.0
train_percent = 0.9
imgfilepath = root + opt.img_path
txtsavepath = root + opt.txt_path
total_img = os.listdir(imgfilepath)
if not os.path.exists(txtsavepath):os.makedirs(txtsavepath)num = len(total_img)
list_index = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list_index, tv)
train = random.sample(trainval, tr)try:os.remove(txtsavepath + '/trainval.txt')os.remove(txtsavepath + '/train.txt')os.remove(txtsavepath + '/valid.txt')
except:pass
file_trainval = open(txtsavepath + '/trainval.txt', 'w')
file_train = open(txtsavepath + '/train.txt', 'w')
file_val = open(txtsavepath + '/valid.txt', 'w')for i in list_index:name = total_img[i] + '\n'if i in trainval:file_trainval.write(imgfilepath + '/' + name)if i in train:file_train.write(imgfilepath + '/' + name)else:file_val.write(imgfilepath + '/' + name)file_trainval.close()
file_train.close()
file_val.close()
IMAGES為所有需要學習的數據圖片目錄?
數據轉換
VOC XML 轉 YOLO
import os
import shutil
import xml.etree.ElementTree as ET
from tqdm import tqdmsets = ['train', 'valid']
project_name = 'detect-pointer'
classes = ['area_p', 'p0', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9']
# project_name = 'detect-digit'
# classes = ['d0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9',
# 'd10', 'd11', 'd12', 'd13', 'd14', 'd15', 'd16', 'd17', 'd18', 'd19']abs_path = os.getcwd()def convert(size, box):dw = 1. / (size[0])dh = 1. / (size[1])x = (box[0] + box[1]) / 2.0 - 1y = (box[2] + box[3]) / 2.0 - 1w = box[1] - box[0]h = box[3] - box[2]x = x * dww = w * dwy = y * dhh = h * dhreturn x, y, w, hdef convert_annotation(image_id, image_set):in_file = open(r'my_datas\%s\ANNOTATIONS\%s.xml' % (project_name, image_id), encoding='UTF-8')out_file = open(r'my_datas\%s\%s\labels\%s.txt' % (project_name, image_set, image_id), 'w')tree = ET.parse(in_file)root = tree.getroot()size = root.find('size')w = int(size.find('width').text)h = int(size.find('height').text)for obj in root.iter('object'):difficult = obj.find('difficult').textcls = obj.find('name').textif cls not in classes or int(difficult) == 1:continuecls_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))b1, b2, b3, b4 = b# 標注越界修正if b2 > w:b2 = wif b4 > h:b4 = hb = (b1, b2, b3, b4)bb = convert((w, h), b)out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')# 刪除文件夾中所有文件
def del_all_file(path):for root, dirs, files in os.walk(path):for f in files:os.remove(os.path.join(root, f))del_all_file(f'my_datas\\{project_name}\\train\images')
print('train\images delete success.')
del_all_file(f'my_datas\\{project_name}\\train\labels')
print('train\labels delete success.')
del_all_file(f'my_datas\\{project_name}\\valid\images')
print('valid\images delete success.')
del_all_file(f'my_datas\\{project_name}\\valid\labels')
print('valid\labels delete success.')for image_set in sets:image_files = open(r'my_datas\%s\TXT_LABELS\%s.txt' % (project_name, image_set)).read().strip().split()for image_file in tqdm(image_files):image_id = os.path.basename(image_file)[:-4]convert_annotation(image_id, image_set)shutil.copyfile(image_file, r'my_datas\%s\%s\images\%s' % (project_name,image_set, os.path.basename(image_file)))
JSON 轉 YOLO
import json
import os
import shutil
from tqdm import tqdmimport numpy as npproject_name = 'pointer-seg'
sets = ['train', 'valid']
classes = ['pointer', 'circle_area']
abs_path = os.getcwd()def convert(size, box):dw = 1. / (size[0])dh = 1. / (size[1])x = (box[0] + box[1]) / 2.0 - 1y = (box[2] + box[3]) / 2.0 - 1w = box[1] - box[0]h = box[3] - box[2]x = x * dww = w * dwy = y * dhh = h * dhreturn x, y, w, hdef pointer_distance(x1, y1, x2, y2):dis = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)return disdef json_to_yolo(path):with open(path, encoding='UTF-8') as f:labelme_data = json.load(f)width = labelme_data["imageWidth"]height = labelme_data["imageHeight"]yolo_lines = []for shape in labelme_data["shapes"]:if shape['shape_type'] == 'polygon':# 多邊形label = shape["label"]points = shape["points"]class_idx = classes.index(label)txt_string = f"{class_idx} "for x, y in points:x /= widthy /= heighttxt_string += f"{x} {y} "yolo_lines.append(txt_string.strip() + "\n")elif shape['shape_type'] == 'circle':# 圓label = shape["label"]points = shape["points"]class_idx = classes.index(label)txt_string = f"{class_idx} "# 圓心a, b = points[0]# 計算半徑r = pointer_distance(a, b, points[1][0], points[1][1])# 生成一些在圓上的點X = np.linspace(a - r, a + r - 1, 15)f = lambda x: np.sqrt(r ** 2 - (x - a) ** 2) + by1 = f(X)y2 = 2 * b - y1c_points = []for i, x in enumerate(X):c_points.append([x, y1[i]])c_points.append([x, y2[i]])for x, y in c_points:x /= widthy /= heighttxt_string += f"{x} {y} "yolo_lines.append(txt_string.strip() + "\n")return yolo_linesdef convert_annotation(image_id, image_set):lines = json_to_yolo(r'my_datas\%s\ANNOTATIONS\%s.json' % (project_name, image_id))with open(r'my_datas\%s\%s\labels\%s.txt' % (project_name, image_set, image_id), 'w') as out_file:out_file.writelines(lines)def del_all_file(path):for root, dirs, files in os.walk(path):for f in files:os.remove(os.path.join(root, f))del_all_file(f'my_datas\\{project_name}\\train\images')
print('train\images delete success.')
del_all_file(f'my_datas\\{project_name}\\train\labels')
print('train\labels delete success.')
del_all_file(f'my_datas\\{project_name}\\valid\images')
print('valid\images delete success.')
del_all_file(f'my_datas\\{project_name}\\valid\labels')
print('valid\labels delete success.')for image_set in sets:image_files = open(r'my_datas\%s\TXT_LABELS\%s.txt' % (project_name, image_set)).read().strip().split()for image_file in tqdm(image_files):image_id = os.path.basename(image_file)[:-4]convert_annotation(image_id, image_set)shutil.copyfile(image_file, r'my_datas\%s\%s\images\%s' % (project_name,image_set, os.path.basename(image_file)))
完整目錄結構:
至此,用于深度學習所需的所有訓練、驗證數據已準備好,下一篇開始基于Utralytics YOLO系列進行訓練和預測。
【工業機器視覺】基于深度學習的儀表盤識讀(讀數識別)(4)-CSDN博客