项目参考AAAI Association for the Advancement of Artificial Intelligence
研究背景与意义:
随着计算机视觉技术的不断发展,人们对于图像分割的需求也越来越迫切。图像分割是计算机视觉领域的一个重要研究方向,它的目标是将图像中的不同区域进行分割,以便更好地理解和处理图像。其中,掌纹ROI区域分割是图像分割中的一个重要任务,它可以用于掌纹识别、生物特征识别等领域。
然而,传统的图像分割方法在掌纹ROI区域分割任务上存在一些问题。首先,传统的方法往往需要大量的手工设计特征,这使得算法的泛化能力较差,对于不同的数据集和场景很难取得良好的效果。其次,传统的方法在处理复杂的背景和噪声时表现不佳,容易受到干扰而产生误分割。此外,传统的方法通常需要较长的计算时间,无法满足实时性的要求。
为了解决上述问题,近年来,深度学习技术在图像分割领域取得了显著的进展。特别是卷积神经网络(CNN)的出现,极大地推动了图像分割的发展。然而,传统的CNN模型在处理掌纹ROI区域分割任务时仍然存在一些挑战。首先,传统的CNN模型往往需要大量的计算资源和训练数据,对于一些资源有限的场景来说,这是一个不可忽视的问题。其次,传统的CNN模型在处理图像中的细节信息时表现不佳,容易产生边界模糊的问题。
因此,本研究提出了一种新的掌纹ROI区域分割系统,该系统融合了分布移位卷积(DSConv)和YOLO(You Only Look Once)算法。DSConv是一种新型的卷积操作,它可以在保持计算效率的同时,提高模型对细节信息的感知能力。而YOLO算法是一种实时目标检测算法,它可以快速准确地定位和识别图像中的目标。
本研究的主要目标是设计一个高效准确的掌纹ROI区域分割系统,以满足实时性和准确性的要求。具体来说,本研究的主要工作包括以下几个方面:首先,我们将设计一个基于DSConv的卷积神经网络模型,用于提取图像中的特征信息。其次,我们将引入YOLO算法,用于定位和识别掌纹ROI区域。最后,我们将通过大量的实验验证和比较,评估所提出的系统在掌纹ROI区域分割任务上的性能。
本研究的意义主要体现在以下几个方面:首先,所提出的掌纹ROI区域分割系统可以在保持较高准确性的同时,提高计算效率,满足实时性的要求。其次,所提出的系统可以更好地处理复杂的背景和噪声,提高分割的鲁棒性。最后,本研究的成果可以为掌纹识别、生物特征识别等领域的研究和应用提供有力的支持。
综上所述,本研究将融合分布移位卷积(DSConv)和YOLO算法,设计一个高效准确的掌纹ROI区域分割系统。该系统将在实时性、准确性和鲁棒性等方面取得显著的改进,为掌纹识别、生物特征识别等领域的研究和应用提供有力的支持。
融合分布移位卷积(DSConv)YOLO的掌纹ROI区域分割系统_哔哩哔哩_bilibili
首先,我们需要收集所需的图片。这可以通过不同的方式来实现,例如使用现有的公开数据集FrictionDatasets。
eiseg是一个图形化的图像注释工具,支持COCO和YOLO格式。以下是使用eiseg将图片标注为COCO格式的步骤:
(1)下载并安装eiseg。
(2)打开eiseg并选择“Open Dir”来选择你的图片目录。
(3)为你的目标对象设置标签名称。
(4)在图片上绘制矩形框,选择对应的标签。
(5)保存标注信息,这将在图片目录下生成一个与图片同名的JSON文件。
(6)重复此过程,直到所有的图片都标注完毕。
由于YOLO使用的是txt格式的标注,我们需要将VOC格式转换为YOLO格式。可以使用各种转换工具或脚本来实现。
下面是一个简单的方法是使用Python脚本,该脚本读取XML文件,然后将其转换为YOLO所需的txt格式。
import contextlib import json import cv2 import pandas as pd from PIL import Image from collections import defaultdict from utils import * # Convert INFOLKS JSON file into YOLO-format labels ---------------------------- def convert_infolks_json(name, files, img_path): # Create folders path = make_dirs() # Import json data = [] for file in glob.glob(files): with open(file) as f: jdata = json.load(f) jdata['json_file'] = file data.append(jdata) # Write images and shapes name = path + os.sep + name file_id, file_name, wh, cat = [], [], [], [] for x in tqdm(data, desc='Files and Shapes'): f = glob.glob(img_path + Path(x['json_file']).stem + '.*')[0] file_name.append(f) wh.append(exif_size(Image.open(f))) # (width, height) cat.extend(a['classTitle'].lower() for a in x['output']['objects']) # categories # filename with open(name + '.txt', 'a') as file: file.write('%sn' % f) # Write *.names file names = sorted(np.unique(cat)) # names.pop(names.index('Missing product')) # remove with open(name + '.names', 'a') as file: [file.write('%sn' % a) for a in names] # Write labels file for i, x in enumerate(tqdm(data, desc='Annotations')): label_name = Path(file_name[i]).stem + '.txt' with open(path + '/labels/' + label_name, 'a') as file: for a in x['output']['objects']: # if a['classTitle'] == 'Missing product': # continue # skip category_id = names.index(a['classTitle'].lower()) # The INFOLKS bounding box format is [x-min, y-min, x-max, y-max] box = np.array(a['points']['exterior'], dtype=np.float32).ravel() box[[0, 2]] /= wh[i][0] # normalize x by width box[[1, 3]] /= wh[i][1] # normalize y by height box = [box[[0, 2]].mean(), box[[1, 3]].mean(), box[2] - box[0], box[3] - box[1]] # xywh if (box[2] > 0.) and (box[3] > 0.): # if w > 0 and h > 0 file.write('%g %.6f %.6f %.6f %.6fn' % (category_id, *box)) # Split data into train, test, and validate files split_files(name, file_name) write_data_data(name + '.data', nc=len(names)) print(f'Done. Output saved to {os.getcwd() + os.sep + path}') # Convert vott JSON file into YOLO-format labels ------------------------------- def convert_vott_json(name, files, img_path): # Create folders path = make_dirs() name = path + os.sep + name # Import json data = [] for file in glob.glob(files): with open(file) as f: jdata = json.load(f) jdata['json_file'] = file data.append(jdata) # Get all categories file_name, wh, cat = [], [], [] for i, x in enumerate(tqdm(data, desc='Files and Shapes')): with contextlib.suppress(Exception): cat.extend(a['tags'][0] for a in x['regions']) # categories # Write *.names file names = sorted(pd.unique(cat)) with open(name + '.names', 'a') as file: [file.write('%sn' % a) for a in names] # Write labels file n1, n2 = 0, 0 missing_images = [] for i, x in enumerate(tqdm(data, desc='Annotations')): f = glob.glob(img_path + x['asset']['name'] + '.jpg') if len(f): f = f[0] file_name.append(f) wh = exif_size(Image.open(f)) # (width, height) n1 += 1 if (len(f) > 0) and (wh[0] > 0) and (wh[1] > 0): n2 += 1 # append filename to list with open(name + '.txt', 'a') as file: file.write('%sn' % f) # write labelsfile label_name = Path(f).stem + '.txt' with open(path + '/labels/' + label_name, 'a') as file: for a in x['regions']: category_id = names.index(a['tags'][0]) # The INFOLKS bounding box format is [x-min, y-min, x-max, y-max] box = a['boundingBox'] box = np.array([box['left'], box['top'], box['width'], box['height']]).ravel() box[[0, 2]] /= wh[0] # normalize x by width box[[1, 3]] /= wh[1] # normalize y by height box = [box[0] + box[2] / 2, box[1] + box[3] / 2, box[2], box[3]] # xywh if (box[2] > 0.) and (box[3] > 0.): # if w > 0 and h > 0 file.write('%g %.6f %.6f %.6f %.6fn' % (category_id, *box)) else: missing_images.append(x['asset']['name']) print('Attempted %g json imports, found %g images, imported %g annotations successfully' % (i, n1, n2)) if len(missing_images): print('WARNING, missing images:', missing_images) # Split data into train, test, and validate files split_files(name, file_name) print(f'Done. Output saved to {os.getcwd() + os.sep + path}') # Convert ath JSON file into YOLO-format labels -------------------------------- def convert_ath_json(json_dir): # dir contains json annotations and images # Create folders dir = make_dirs() # output directory jsons = [] for dirpath, dirnames, filenames in os.walk(json_dir): jsons.extend( os.path.join(dirpath, filename) for filename in [ f for f in filenames if f.lower().endswith('.json') ] ) # Import json n1, n2, n3 = 0, 0, 0 missing_images, file_name = [], [] for json_file in sorted(jsons): with open(json_file) as f: data = json.load(f) # # Get classes # try: # classes = list(data['_via_attributes']['region']['class']['options'].values()) # classes # except: # classes = list(data['_via_attributes']['region']['Class']['options'].values()) # classes # # Write *.names file # names = pd.unique(classes) # preserves sort order # with open(dir + 'data.names', 'w') as f: # [f.write('%sn' % a) for a in names] # Write labels file for x in tqdm(data['_via_img_metadata'].values(), desc=f'Processing {json_file}'): image_file = str(Path(json_file).parent / x['filename']) f = glob.glob(image_file) # image file if len(f): f = f[0] file_name.append(f) wh = exif_size(Image.open(f)) # (width, height) n1 += 1 # all images if len(f) > 0 and wh[0] > 0 and wh[1] > 0: label_file = dir + 'labels/' + Path(f).stem + '.txt' nlabels = 0 try: with open(label_file, 'a') as file: # write labelsfile # try: # category_id = int(a['region_attributes']['class']) # except: # category_id = int(a['region_attributes']['Class']) category_id = 0 # single-class for a in x['regions']: # bounding box format is [x-min, y-min, x-max, y-max] box = a['shape_attributes'] box = np.array([box['x'], box['y'], box['width'], box['height']], dtype=np.float32).ravel() box[[0, 2]] /= wh[0] # normalize x by width box[[1, 3]] /= wh[1] # normalize y by height box = [box[0] + box[2] / 2, box[1] + box[3] / 2, box[2], box[3]] # xywh (left-top to center x-y) if box[2] > 0. and box[3] > 0.: # if w > 0 and h > 0 file.write('%g %.6f %.6f %.6f %.6fn' % (category_id, *box)) n3 += 1 nlabels += 1 if nlabels == 0: # remove non-labelled images from dataset os.system(f'rm {label_file}') # print('no labels for %s' % f) continue # next file # write image img_size = 4096 # resize to maximum img = cv2.imread(f) # BGR assert img is not None, 'Image Not Found ' + f r = img_size / max(img.shape) # size ratio if r < 1: # downsize if necessary h, w, _ = img.shape img = cv2.resize(img, (int(w * r), int(h * r)), interpolation=cv2.INTER_AREA) ifile = dir + 'images/' + Path(f).name if cv2.imwrite(ifile, img): # if success append image to list with open(dir + 'data.txt', 'a') as file: file.write('%sn' % ifile) n2 += 1 # correct images except Exception: os.system(f'rm {label_file}') print(f'problem with {f}') else: missing_images.append(image_file) nm = len(missing_images) # number missing print('nFound %g JSONs with %g labels over %g images. Found %g images, labelled %g images successfully' % (len(jsons), n3, n1, n1 - nm, n2)) if len(missing_images): print('WARNING, missing images:', missing_images) # Write *.names file names = ['knife'] # preserves sort order with open(dir + 'data.names', 'w') as f: [f.write('%sn' % a) for a in names] # Split data into train, test, and validate files split_rows_simple(dir + 'data.txt') write_data_data(dir + 'data.data', nc=1) print(f'Done. Output saved to {Path(dir).absolute()}') def convert_coco_json(json_dir='../coco/annotations/', use_segments=False, cls91to80=False): save_dir = make_dirs() # output directory coco80 = coco91_to_coco80_class() # Import json for json_file in sorted(Path(json_dir).resolve().glob('*.json')): fn = Path(save_dir) / 'labels' / json_file.stem.replace('instances_', '') # folder name fn.mkdir() with open(json_file) as f: data = json.load(f) # Create image dict images = {'%g' % x['id']: x for x in data['images']} # Create image-annotations dict imgToAnns = defaultdict(list) for ann in data['annotations']: imgToAnns[ann['image_id']].append(ann) # Write labels file for img_id, anns in tqdm(imgToAnns.items(), desc=f'Annotations {json_file}'): img = images['%g' % img_id] h, w, f = img['height'], img['width'], img['file_name'] bboxes = [] segments = [] for ann in anns: if ann['iscrowd']: continue # The COCO box format is [top left x, top left y, width, height] box = np.array(ann['bbox'], dtype=np.float64) box[:2] += box[2:] / 2 # xy top-left corner to center box[[0, 2]] /= w # normalize x box[[1, 3]] /= h # normalize y if box[2] <= 0 or box[3] <= 0: # if w <= 0 and h <= 0 continue cls = coco80[ann['category_id'] - 1] if cls91to80 else ann['category_id'] - 1 # class box = [cls] + box.tolist() if box not in bboxes: bboxes.append(box) # Segments if use_segments: if len(ann['segmentation']) > 1: s = merge_multi_segment(ann['segmentation']) s = (np.concatenate(s, axis=0) / np.array([w, h])).reshape(-1).tolist() else: s = [j for i in ann['segmentation'] for j in i] # all segments concatenated s = (np.array(s).reshape(-1, 2) / np.array([w, h])).reshape(-1).tolist() s = [cls] + s if s not in segments: segments.append(s) # Write with open((fn / f).with_suffix('.txt'), 'a') as file: for i in range(len(bboxes)): line = *(segments[i] if use_segments else bboxes[i]), # cls, box or segments file.write(('%g ' * len(line)).rstrip() % line + 'n') def min_index(arr1, arr2): """Find a pair of indexes with the shortest distance. Args: arr1: (N, 2). arr2: (M, 2). Return: a pair of indexes(tuple). """ dis = ((arr1[:, None, :] - arr2[None, :, :]) ** 2).sum(-1) return np.unravel_index(np.argmin(dis, axis=None), dis.shape) def merge_multi_segment(segments): """Merge multi segments to one list. Find the coordinates with min distance between each segment, then connect these coordinates with one thin line to merge all segments into one. Args: segments(List(List)): original segmentations in coco's json file. like [segmentation1, segmentation2,...], each segmentation is a list of coordinates. """ s = [] segments = [np.array(i).reshape(-1, 2) for i in segments] idx_list = [[] for _ in range(len(segments))] # record the indexes with min distance between each segment for i in range(1, len(segments)): idx1, idx2 = min_index(segments[i - 1], segments[i]) idx_list[i - 1].append(idx1) idx_list[i].append(idx2) # use two round to connect all the segments for k in range(2): # forward connection if k == 0: for i, idx in enumerate(idx_list): # middle segments have two indexes # reverse the index of middle segments if len(idx) == 2 and idx[0] > idx[1]: idx = idx[::-1] segments[i] = segments[i][::-1, :] segments[i] = np.roll(segments[i], -idx[0], axis=0) segments[i] = np.concatenate([segments[i], segments[i][:1]]) # deal with the first segment and the last one if i in [0, len(idx_list) - 1]: s.append(segments[i]) else: idx = [0, idx[1] - idx[0]] s.append(segments[i][idx[0]:idx[1] + 1]) else: for i in range(len(idx_list) - 1, -1, -1): if i not in [0, len(idx_list) - 1]: idx = idx_list[i] nidx = abs(idx[1] - idx[0]) s.append(segments[i][nidx:]) return s def delete_dsstore(path='../datasets'): # Delete apple .DS_store files from pathlib import Path files = list(Path(path).rglob('.DS_store')) print(files) for f in files: f.unlink() if __name__ == '__main__': source = 'COCO' if source == 'COCO': convert_coco_json('./annotations', # directory with *.json use_segments=True, cls91to80=True) elif source == 'infolks': # Infolks https://infolks.info/ convert_infolks_json(name='out', files='../data/sm4/json/*.json', img_path='../data/sm4/images/') elif source == 'vott': # VoTT https://github.com/microsoft/VoTT convert_vott_json(name='data', files='../../Downloads/athena_day/20190715/*.json', img_path='../../Downloads/athena_day/20190715/') # images folder elif source == 'ath': # ath format convert_ath_json(json_dir='../../Downloads/athena/') # images folder # zip results # os.system('zip -r ../coco.zip ../coco')
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 整理数据文件夹结构我们需要将数据集整理为以下结构:
-----datasets-----coco128-seg |-----images | |-----train | |-----valid | |-----test | |-----labels | |-----train | |-----valid | |-----test | 12345678910111213 模型训练
Epoch gpu_mem box obj cls labels img_size 1/200 20.8G 0.01576 0.01955 0.007536 22 1280: 100%|██████████| 849/849 [14:42<00:00, 1.04s/it] Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:14<00:00, 2.87it/s] all 3395 17314 0.994 0.957 0.0957 0.0843 Epoch gpu_mem box obj cls labels img_size 2/200 20.8G 0.01578 0.01923 0.007006 22 1280: 100%|██████████| 849/849 [14:44<00:00, 1.04s/it] Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:12<00:00, 2.95it/s] all 3395 17314 0.996 0.956 0.0957 0.0845 Epoch gpu_mem box obj cls labels img_size 3/200 20.8G 0.01561 0.0191 0.006895 27 1280: 100%|██████████| 849/849 [10:56<00:00, 1.29it/s] Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|███████ | 187/213 [00:52<00:00, 4.04it/s] all 3395 17314 0.996 0.957 0.0957 0.0845 1234567891011121314
def export_formats(): # YOLOv5 export formats x = [ ['PyTorch', '-', '.pt', True, True], ['TorchScript', 'torchscript', '.torchscript', True, True], ['ONNX', 'onnx', '.onnx', True, True], ['OpenVINO', 'openvino', '_openvino_model', True, False], ['TensorRT', 'engine', '.engine', False, True], ['CoreML', 'coreml', '.mlmodel', True, False], ['TensorFlow SavedModel', 'saved_model', '_saved_model', True, True], ['TensorFlow GraphDef', 'pb', '.pb', True, True], ['TensorFlow Lite', 'tflite', '.tflite', True, False], ['TensorFlow Edge TPU', 'edgetpu', '_edgetpu.tflite', False, False], ['TensorFlow.js', 'tfjs', '_web_model', False, False], ['PaddlePaddle', 'paddle', '_paddle_model', True, True],] return pd.DataFrame(x, columns=['Format', 'Argument', 'Suffix', 'CPU', 'GPU']) def try_export(inner_func): # YOLOv5 export decorator, i..e @try_export inner_args = get_default_args(inner_func) def outer_func(*args, **kwargs): prefix = inner_args['prefix'] try: with Profile() as dt: f, model = inner_func(*args, **kwargs) LOGGER.info(f'{prefix} export success ✅ {dt.t:.1f}s, saved as {f} ({file_size(f):.1f} MB)') return f, model except Exception as e: LOGGER.info(f'{prefix} export failure ❌ {dt.t:.1f}s: {e}') return None, None return outer_func @try_export def export_torchscript(model, im, file, optimize, prefix=colorstr('TorchScript:')): # YOLOv5 TorchScript model export LOGGER.info(f'n{prefix} starting export with torch {torch.__version__}...') f = file.with_suffix('.torchscript') ts = torch.jit.trace(model, im, strict=False) d = {"shape": im.shape, "stride": int(max(model.stride)), "names": model.names} extra_files = {'config.txt': json.dumps(d)} # torch._C.ExtraFilesMap() if optimize: # https://pytorch.org/tutorials/recipes/mobile_interpreter.html optimize_for_mobile(ts)._save_for_lite_interpreter(str(f), _extra_files=extra_files) else: ts.save(str(f), _extra_files=extra_files) return f, None @try_export def export_onnx(model, im, file, opset, dynamic, simplify, prefix=colorstr('ONNX:')): # YOLOv5 ONNX export check_requirements('onnx>=1.12.0') import onnx LOGGER.info(f'n{prefix} starting export with onnx {onnx.__version__}...') f = file.with_suffix('.onnx') output_names = ['output0', 'output1'] if isinstance(model, SegmentationModel) else ['output0'] if dynamic: dynamic = {'images': {0: 'batch', 2: 'height', 3: 'width'}} # shape(1,3,640,640) if isinstance(model, SegmentationModel): dynamic['output0'] = {0: 'batch', 1: 'anchors'} # shape(1,25200,85) dynamic['output1'] = {0: 'batch', 2: 'mask_height', 3: 'mask_width'} # shape(1,32,160,160) elif isinstance(model, DetectionModel): dynamic['output0'] = {0: 'batch', 1: 'anchors'} # shape(1,25200,85) torch.onnx.export( model.cpu() if dynamic else model, # --dynamic only compatible with cpu im.cpu() if dynamic else im, f, verbose=False, opset_version=opset, do_constant_folding=True, # WARNING: DNN inference with torch>=1.12 may require do_constant_folding=False input_names=['images'], output_names=output_names, dynamic_axes=dynamic or None) # Checks model_onnx = onnx.load(f) # load onnx model onnx.checker.check_model(model_onnx) # check onnx model # Metadata d = {'stride': int(max(model.stride)), 'names': model.names} for k, v in d.items(): meta = model_onnx.metadata_props.add() meta.key, meta.value = k, str(v) onnx.save(model_onnx, f) # Simplify if simplify: try: cuda = torch.cuda.is_available() check_requirements(('onnxruntime-gpu' if cuda else 'onnxruntime', 'onnx-simplifier>=0.4.1')) import onnxsim LOGGER.info(f'{prefix} simplifying with onnx-simplifier {onnxsim.__version__}...') model_simp, check = onnxsim.simplify(f, check=True) assert check, 'assert check failed' onnx.save(model_simp, f) except Exception as e: LOGGER.info(f'{prefix} simplifier failure ❌ {e}') return f, None
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109export.py是一个用于将YOLOv5 PyTorch模型导出为其他格式的程序文件。该文件提供了多种导出格式选项,包括TorchScript、ONNX、OpenVINO、TensorRT、CoreML、TensorFlow SavedModel、TensorFlow GraphDef、TensorFlow Lite、TensorFlow Edge TPU、TensorFlow.js和PaddlePaddle。用户可以根据需要选择要导出的格式,并使用相应的命令行参数运行export.py文件来进行导出。
export.py文件还包含了一些辅助函数和装饰器,用于处理模型导出过程中的异常情况和记录导出结果。具体的导出过程会根据选择的导出格式和模型类型进行相应的处理。例如,对于TorchScript格式的导出,会使用torch.jit.trace函数将模型转换为TorchScript格式,并保存为.torchscript文件;对于ONNX格式的导出,会使用torch.onnx.export函数将模型转换为ONNX格式,并保存为.onnx文件。
在导出过程中,export.py文件会检查所需的依赖库是否已安装,并根据需要进行安装。导出成功后,会输出导出结果的信息,包括导出所花费的时间和导出文件的大小。
此外,export.py文件还提供了一个export_formats函数,用于返回支持的导出格式的信息。
5.2 ui.pyclass YOLOv5Detector: def __init__(self, weights='./best.pt', data=ROOT / 'data/coco128.yaml', device='', half=False, dnn=False): self.model, self.stride, self.names, self.pt = self.load_model(weights, data, device, half, dnn) def load_model(self, weights, data, device, half, dnn): device = select_device(device) model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half) stride, names, pt = model.stride, model.names, model.pt return model, stride, names, pt def run(self, img, imgsz=(640, 640), conf_thres=0.25, iou_thres=0.45, max_det=1000, device='', classes=None, agnostic_nms=False, augment=False, half=False, retina_masks=True): imgsz = check_img_size(imgsz, s=self.stride) # check image size self.model.warmup(imgsz=(1 if self.pt else 1, 3, *imgsz)) # warmup cal_detect = [] device = select_device(device) names = self.model.module.names if hasattr(self.model, 'module') else self.model.names # get class names # Set Dataloader im = letterbox(img, imgsz, self.stride, self.pt)[0] # Convert im = im.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGB im = np.ascontiguousarray(im) im = torch.from_numpy(im).to(device) im = im.half() if half else im.float() # uint8 to fp .....
12345678910111213141516171819202122232425262728293031这个程序文件是一个使用PyQt5库创建的图形用户界面(GUI)应用程序。它包含了一个主窗口,其中有一个标签用于显示原始图像,另一个标签用于显示输出图像。还有一个文本浏览器用于显示程序的输出信息。主窗口还包含三个按钮,分别用于选择文件、开始识别和退出系统。
程序文件中还导入了其他的Python库,包括PyQt5的各个模块、os、sys、pathlib、numpy、time、argparse、torch、cv2等。这些库用于实现图形界面的创建、文件操作、模型加载、图像处理等功能。
程序的主要功能是使用已经训练好的模型对图像进行目标检测。通过选择文件按钮,用户可以选择要检测的图像文件。然后,点击开始识别按钮,程序会调用模型进行目标检测,并将结果显示在输出标签中。如果没有选择文件,程序会自动打开摄像头进行实时目标检测。
在目标检测过程中,程序会将检测到的目标在原始图像上用矩形框标出,并在输出图像中显示检测结果。同时,程序会将检测到的目标的类别、置信度和轮廓信息保存在一个列表中,并在文本浏览器中显示出来。
用户可以通过退出系统按钮退出程序。
总之,这个程序文件是一个基于PyQt5的图像目标检测应用程序,可以选择图像文件或使用摄像头进行实时目标检测,并将检测结果显示在图形界面中。
5.3 val.pyclass YOLOv5Validator: def __init__(self, weights, data, batch_size=32, imgsz=640, conf_thres=0.001, iou_thres=0.6, max_det=300, task='val', device='', workers=8, single_cls=False, augment=False, verbose=False, save_txt=False, save_hybrid=False, save_conf=False, save_json=False, project=ROOT / 'runs/val', name='exp', exist_ok=False, half=True, dnn=False, model=None, dataloader=None, save_dir=Path(''), plots=True, callbacks=Callbacks(), compute_loss=None): self.weights = weights self.data = data self.batch_size = batch_size self.imgsz = imgsz self.conf_thres = conf_thres self.iou_thres = iou_thres self.max_det = max_det self.task = task self.device = device self.workers = workers self.single_cls = single_cls self.augment = augment self.verbose = verbose self.save_txt = save_txt self.save_hybrid = save_hybrid self.save_conf = save_conf self.save_json = save_json self.project = project self.name = name self.exist_ok = exist_ok self.half = half self.dnn = dnn self.model = model self.dataloader = dataloader self.save_dir = save_dir self.plots = plots self.callbacks = callbacks self.compute_loss = compute_loss def run(self): # Initialize/load model and set device training = self.model is not None if training: # called by train.py device, pt, jit, engine = next(self.model.parameters()).device, True, False, False # get model device, PyTorch model half &= device.type != 'cpu' # half precision only supported on CUDA self.model.half() if half else self.model.float() else: # called directly device = select_device(self.device, batch_size=self.batch_size) # Directories self.save_dir = increment_path(Path(self.project) / self.name, exist_ok=self.exist_ok) # increment run (self.save_dir / 'labels' if self.save_txt else self.save_dir).mkdir(parents=True, exist_ok=True) # make dir # Load model self.model = DetectMultiBackend(self.weights, device=device, dnn=self.dnn, data=self.data, fp16=self.half) stride, pt, jit, engine = self.model.stride, self.model.pt, self.model.jit, self.model.engine self.imgsz = check_img_size(self.imgsz, s=stride) # check image size self.half = self.model.fp16 # FP16 supported on limited backends with CUDA if engine: self.batch_size = self.model.batch_size else: device = self.model.device if not (pt or jit): self.batch_size = 1 # export.py models default to batch-size 1 LOGGER.info(f'Forcing --batch-size 1 square inference (1,3,{self.imgsz},{self.imgsz}) for non-PyTorch models') # Data self.data = check_dataset(self.data) # check # Configure self.model.eval() cuda = device.type != 'cpu' is_coco = isinstance(self.data.get('val'), str) and self.data['val'].endswith(f'coco{os.sep}val2017.txt') # COCO dataset nc = 1 if self.single_cls else int(self.data['nc']) # number of classes iouv = torch.linspace(0.5, 0.95, 10, device=device) # iou vector for mAP@0.5:0.95 niou = iouv.numel() # Dataloader if not training: if pt and not self.single_cls: # check --weights are trained on --data ncm = self.model.model.nc assert ncm == nc, f'{self.weights} ({ncm} classes) trained on different --data than what you passed ({nc} ' f'classes). Pass correct combination of --weights and --data that are trained together.' self.model.warmup(imgsz=(1 if pt else self.batch_size, 3, self.imgsz, self.imgsz)) # warmup pad, rect = (0.0, False) if self.task == 'speed' else (0.5, pt) # square inference for benchmarks self.task = self.task if self.task in ('train', 'val', 'test') else 'val' # path to train/val/test images self.dataloader = create_dataloader(self.data[self.task], self.imgsz, self.batch_size, stride, self.single_cls, pad=pad, rect=rect, workers=self.workers, prefix=colorstr(f'{self.task}: '))[0] seen = 0 confusion_matrix = ConfusionMatrix(nc=nc) names = self.model.names if hasattr(self.model, 'names') else self.model.module.names # get class names if isinstance(names, (list, tuple)): # old format names = dict(enumerate(names)) class_map = coco80_to_coco91_class() if is_coco else list(range(1000)) s = ('%22s' + '%11s' * 6) % ('Class', 'Images', 'Instances', 'P', 'R', 'mAP50', 'mAP50-95') tp, fp, p, r, f1, mp, mr, map50, ap50, map = 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596val.py是一个用于在检测数据集上验证训练好的YOLOv5检测模型的程序文件。它可以根据指定的参数加载模型并在指定的数据集上进行推理和评估。程序文件中包含了一些函数和类,用于处理数据、进行推理、计算损失、绘制图像等操作。程序文件还包含了一些命令行参数,用于指定模型权重文件、数据集文件、推理图像大小等参数。
5.4 yolov5-DSConv.pyclass DSConv(_ConvNd): def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=None, dilation=1, groups=1, padding_mode='zeros', bias=False, block_size=32, KDSBias=False, CDS=False): padding = _pair(autopad(kernel_size, padding, dilation)) kernel_size = _pair(kernel_size) stride = _pair(stride) dilation = _pair(dilation) blck_numb = math.ceil(((in_channels)/(block_size*groups))) super(DSConv, self).__init__( in_channels, out_channels, kernel_size, stride, padding, dilation, False, _pair(0), groups, bias, padding_mode) # KDS weight From Paper self.intweight = torch.Tensor(out_channels, in_channels, *kernel_size) self.alpha = torch.Tensor(out_channels, blck_numb, *kernel_size) # KDS bias From Paper self.KDSBias = KDSBias self.CDS = CDS if KDSBias: self.KDSb = torch.Tensor(out_channels, blck_numb, *kernel_size) if CDS: self.CDSw = torch.Tensor(out_channels) self.CDSb = torch.Tensor(out_channels) self.reset_parameters() def get_weight_res(self): # Include expansion of alpha and multiplication with weights to include in the convolution layer here alpha_res = torch.zeros(self.weight.shape).to(self.alpha.device) # Include KDSBias if self.KDSBias: KDSBias_res = torch.zeros(self.weight.shape).to(self.alpha.device) # Handy definitions: nmb_blocks = self.alpha.shape[1] total_depth = self.weight.shape[1] bs = total_depth//nmb_blocks llb = total_depth-(nmb_blocks-1)*bs # Casting the Alpha values as same tensor shape as weight for i in range(nmb_blocks): length_blk = llb if i==nmb_blocks-1 else bs shp = self.alpha.shape # Notice this is the same shape for the bias as well to_repeat=self.alpha[:, i, ...].view(shp[0],1,shp[2],shp[3]).clone() repeated = to_repeat.expand(shp[0], length_blk, shp[2], shp[3]).clone() alpha_res[:, i*bs:(i*bs+length_blk), ...] = repeated.clone() if self.KDSBias: to_repeat = self.KDSb[:, i, ...].view(shp[0], 1, shp[2], shp[3]).clone() repeated = to_repeat.expand(shp[0], length_blk, shp[2], shp[3]).clone() KDSBias_res[:, i*bs:(i*bs+length_blk), ...] = repeated.clone() if self.CDS: to_repeat = self.CDSw.view(-1, 1, 1, 1) repeated = to_repeat.expand_as(self.weight) print(repeated.shape) # Element-wise multiplication of alpha and weight weight_res = torch.mul(alpha_res, self.weight) if self.KDSBias: weight_res = torch.add(weight_res, KDSBias_res) return weight_res def forward(self, input): # Get resulting weight #weight_res = self.get_weight_res() # Returning convolution return F.conv2d(input, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups) class DSConv2D(nn.Module): def __init__(self, inc, ouc, k=1, s=1, p=None, g=1, d=1, act=True): super().__init__() self.conv = DSConv(inc, ouc, k, s, p, g, d) class Bottleneck_DSConv(nn.Module): # Standard bottleneck def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion super().__init__() c_ = int(c2 * e) # hidden channels self.cv1 = DSConv2D(c1, c_, 1, 1) self.cv2 = DSConv2D(c_, c2, 3, 1, g=g) self.add = shortcut and c1 == c2 def forward(self, x): return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) class C3_DSConv(nn.Module): # C3 module with dsconv def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): super().__init__() c_ = int(c2 * e) self.m = nn.Sequential(*(Bottleneck_DSConv(c_, c_, shortcut, g, e=1.0) for _ in range(n)))
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103这个程序文件是一个实现了DSConv(Depthwise Separable Convolution)的模块。DSConv是一种卷积操作,可以在减少参数数量的同时保持模型的性能。该文件定义了DSConv类和DSConv2D类,以及使用DSConv的Bottleneck_DSConv和C3_DSConv模块。
DSConv类继承自torch.nn.modules.conv._ConvNd类,它接受一些参数,包括输入通道数、输出通道数、卷积核大小、步长、填充、膨胀率、分组数等。在初始化时,它会根据输入参数计算填充大小、卷积核大小、步长和膨胀率等。它还定义了一些权重和偏置参数,以及一些方法用于计算权重和前向传播。
DSConv2D类继承自torch.nn.modules.conv.Conv类,它是DSConv的一个封装,方便在模型中使用。
Bottleneck_DSConv类是一个标准的瓶颈块,它包含了两个DSConv2D层,用于进行特征提取和特征融合。
C3_DSConv类是一个使用DSConv的C3模块,它包含了多个Bottleneck_DSConv块,用于构建深度网络。
整个程序文件实现了DSConv的定义和使用,可以在深度学习模型中替代传统的卷积操作,以减少参数数量并提高模型性能。
5.5 classifypredict.pyclass YOLOv5Classifier: def __init__(self, weights, source, data, imgsz, device, view_img, save_txt, nosave, augment, visualize, update, project, name, exist_ok, half, dnn, vid_stride): self.weights = weights self.source = source self.data = data self.imgsz = imgsz self.device = device self.view_img = view_img self.save_txt = save_txt self.nosave = nosave self.augment = augment self.visualize = visualize self.update = update self.project = project self.name = name self.exist_ok = exist_ok self.half = half self.dnn = dnn self.vid_stride = vid_stride def run(self): source = str(self.source) save_img = not self.nosave and not source.endswith('.txt') # save inference images is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS) is_url = source.lower().startswith(('rtsp://', 'rtmp://', 'http://', 'https://')) webcam = source.isnumeric() or source.endswith('.streams') or (is_url and not is_file) screenshot = source.lower().startswith('screen') if is_url and is_file: source = check_file(source) # download # Directories save_dir = increment_path(Path(self.project) / self.name, exist_ok=self.exist_ok) # increment run (save_dir / 'labels' if self.save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir # Load model device = select_device(self.device) model = DetectMultiBackend(self.weights, device=device, dnn=self.dnn, data=self.data, fp16=self.half) stride, names, pt = model.stride, model.names, model.pt imgsz = check_img_size(self.imgsz, s=stride) # check image size # Dataloader bs = 1 # batch_size if webcam: view_img = check_imshow(warn=True) dataset = LoadStreams(source, img_size=imgsz, transforms=classify_transforms(imgsz[0]), vid_stride=self.vid_stride) bs = len(dataset) elif screenshot: dataset = LoadScreenshots(source, img_size=imgsz, stride=stride, auto=pt) else: dataset = LoadImages(source, img_size=imgsz, transforms=classify_transforms(imgsz[0]), vid_stride=self.vid_stride) vid_path, vid_writer = [None] * bs, [None] * bs # Run inference model.warmup(imgsz=(1 if pt else bs, 3, *imgsz)) # warmup seen, windows, dt = 0, [], (Profile(), Profile(), Profile()) for path, im, im0s, vid_cap, s in dataset: with dt[0]: im = torch.Tensor(im).to(model.device) im = im.half() if model.fp16 else im.float() # uint8 to fp16/32 if len(im.shape) == 3: im = im[None] # expand for batch dim # Inference with dt[1]: results = model(im) # Post-process with dt[2]: pred = F.softmax(results, dim=1) # probabilities # Process predictions for i, prob in enumerate(pred): # per image seen += 1 if webcam: # batch_size >= 1 p, im0, frame = path[i], im0s[i].copy(), dataset.count s += f'{i}: ' else: p, im0, frame = path, im0s.copy(), getattr(dataset, 'frame', 0) p = Path(p) # to Path save_path = str(save_dir / p.name) # im.jpg txt_path = str(save_dir / 'labels' / p.stem) + ('' if dataset.mode == 'image' else f'_{frame}') # im.txt s += '%gx%g ' % im.shape[2:] # print string annotator = Annotator(im0, example=str(names), pil=True) # Print results top5i = prob.argsort(0, descending=True)[:5].tolist() # top 5 indices s += f"{', '.join(f'{names[j]} {prob[j]:.2f}' for j in top5i)}, " # Write results text = 'n'.join(f'{prob[j]:.2f} {names[j]
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697这个程序文件是一个使用YOLOv5模型进行图像分类推断的脚本。它可以在图像、视频、目录、URL、摄像头等多种来源上运行。
该脚本可以接受以下参数:
--weights:模型权重文件的路径,默认为yolov5s-cls.pt。--source:数据源的路径,可以是文件、目录、URL等,默认为data/images。--data:数据集配置文件的路径,默认为data/coco128.yaml。--imgsz:推断时的图像尺寸,默认为[224]。--device:使用的设备,默认为空,即自动选择。--view-img:是否显示结果。--save-txt:是否保存结果到文本文件。--nosave:是否不保存图像/视频。--augment:是否进行增强推断。--visualize:是否可视化特征。--update:是否更新所有模型。--project:保存结果的项目路径,默认为runs/predict-cls。--name:保存结果的名称,默认为exp。--exist-ok:是否允许覆盖已存在的项目/名称。--half:是否使用FP16半精度推断。--dnn:是否使用OpenCV DNN进行ONNX推断。--vid-stride:视频帧率的步长,默认为1。该脚本会加载模型、数据集配置文件,并根据参数进行推断。推断结果可以保存为图像、视频和文本文件,并可以显示在屏幕上。
整体功能和构架概述:
该项目是一个视觉项目,主要目标是实现掌纹ROI区域分割系统。该系统使用了融合分布移位卷积(DSConv)的YOLOv5模型进行目标检测和分类。项目包含了多个程序文件,用于不同的功能,包括模型训练、模型推断、图形界面等。
下面是每个文件的功能概述:
文件路径功能概述export.py将YOLOv5模型导出为其他格式的文件ui.py创建图形用户界面(GUI)应用程序val.py在检测数据集上验证训练好的YOLOv5模型yolov5-DSConv.py实现了DSConv的模块classify/predict.py使用YOLOv5模型进行图像分类推断classify/train.py训练一个基于YOLOv5的分类器模型classify/val.py在验证数据集上验证训练好的分类器模型models/common.py包含了一些通用的模型函数和类models/experimental.py包含了一些实验性的模型函数和类models/tf.py包含了一些与TensorFlow相关的模型函数和类models/yolo.py包含了YOLOv5模型的定义和相关函数models/init.py模型模块的初始化文件segment/predict.py使用分割模型进行图像分割推断segment/train.py训练一个图像分割模型segment/val.py在验证数据集上验证训练好的图像分割模型utils/activations.py包含了一些激活函数的定义utils/augmentations.py包含了一些数据增强的函数和类utils/autoanchor.py包含了自动锚框生成的函数和类utils/autobatch.py包含了自动批处理的函数和类utils/callbacks.py包含了一些回调函数的定义utils/dataloaders.py包含了数据加载器的定义utils/downloads.py包含了一些下载数据集的函数utils/general.py包含了一些通用的辅助函数utils/loss.py包含了一些损失函数的定义utils/metrics.py包含了一些评估指标的定义utils/plots.py包含了一些绘图函数的定义utils/torch_utils.py包含了一些与PyTorch相关的辅助函数utils/triton.py包含了与Triton Inference Server相关的函数和类utils/init.py工具模块的初始化文件utils/aws/resume.py包含了AWS训练恢复的函数和类utils/aws/init.pyAWS模块的初始化文件utils/flask_rest_api/example_request.py包含了Flask REST API的示例请求utils/flask_rest_api/restapi.py包含了Flask REST API的实现utils/loggers/init.py日志记录器模块的初始化文件utils/loggers/clearml/clearml_utils.py包含了ClearML日志记录器的辅助函数utils/loggers/clearml/hpo.py包含了ClearML日志记录器的超参数优化函数utils/loggers/clearml/init.pyClearML日志记录器模块的初始化文件utils/loggers/comet/comet_utils.py包含了Comet日志记录器的辅助函数utils/loggers/comet/hpo.py包含了Comet日志记录器的超参数优化函数utils/loggers/comet/init.pyComet日志记录器模块的初始化文件utils/loggers/wandb/log_dataset.py包含了WandB日志记录器的数据集记录函数utils/loggers/wandb/sweep.py包含了WandB日志记录器的超参数优化函数utils/loggers/wandb/wandb_utils.py包含了WandB日志记录器的辅助函数utils/loggers/wandb/init.pyWandB日志记录器模块的初始化文件utils/segment/augmentations.py包含了图像分割的数据增强函数和类utils/segment/dataloaders.py包含了图像分割的数据加载器的定义utils/segment/general.py包含了图像分割的通用辅助函数utils/segment/loss.py包含了图像分割的损失函数的定义utils/segment/metrics.py包含了图像分割的评估指标的定义utils/segment/plots.py包含了图像分割的绘图函数的定义utils/segment/init.py图像分割模块的初始化文件以上是每个文件的功能概述,根据文件路径进行了分类。
卷积神经网络已被证明在计算机视觉中传统的艰巨任务中是成功的,例如图像分类和目标检测。随着AlexNet的突破,ILSVRC中创建了许多新的拓扑来实现高精度。此类网络的成功不仅将注意力转移到如何做到这一点上,而且还转移到了它运行的速度和记忆效率上。这些模型以具有数百万个参数而闻名,即使使用GPU,它也需要更多的计算时间和比许多应用程序所需的更多的存储空间。
运行卷积神经网络时所需的大部分内存和计算工作都花在了卷积层中,例ResNet50超过90%的时间/内存。这意味着,为了让网络运行得更快更高效,我们必须提高卷积层的计算负载。
考虑到这一点,研究者提出了一种新型的卷积层,我们称之为分布移位卷积(DSConv)。这种类型的层在设计时考虑了两个主要目标:
它应该大大提高标准卷积层的内存效率和速度;
它应该是标准卷积的即插即用替代品,因此它可以直接用于任何卷积神经网络,包括推理和训练。
研究者通过将传统的卷积内核分解为两个组件来实现这一点。其中之一是只有整数值的张量,不可训练,并根据预训练网络中浮点 (FP) 权重的分布进行计算。另一个组件由两个分布移位器张量组成,它们将量化张量的权重定位在模拟原始预训练网络分布的范围内:其中一个移动每个内核的分布,另一个移动每个通道。这些权重可以重新训练,使网络能够适应新的任务和数据集。
可变量化内核(VQK):此张量仅保留可变位长整数值,并且与原始卷积张量具有相同大小的(ch0,chi,k,k),参数值被设置为从原始浮点模型量化,并且一旦设置不能改变,这是DSConv的量化组件。
分布移位:此组件的目的是移动VQK的分布以尝试模仿原始卷积内核的分布。通过使用两个张量转换两个域来实现。第一个张量是内核分布移位器(KDS),他改变每个(1,BLK,1,1)的分布。
例如,给定(128,128,3,3)的原始单精度张量大小,将位大小的超参数设置为2位且块大小设置为64,将保存2位整数的VQK的大小为(128,128,3,3)(量化后的,由单精度变整型),保持FP32编号的内核移位器(KDS)的大小为2*(128,2,3,3),保存Fp32编号的通道移位器的大小为2*(128),在此示例中,卷积内核减少到其原始大小的7%
使用此设置,VQK充当先验,它捕获特定切片应提取的特征类型的本质。
Quantization Procedure量化函数将要量化的网络的比特数作为输入,并将带符号的整数表示来存储。
这是通过首先缩放每个卷积层的权重以使得原始权重w的最大绝对值与上面的量化约束的最大值匹配来实现的。再次步骤之后,将所有权重量化为最接近的整数,然后将新权重wq作为整数值存储到存储器中,以便稍后在训练和推理中使用。
Distribution Shifts分布转移的目的是移动VQK,使得输出和原始权重张量的值是相匹配的,这是通过内核中的分布偏移(KDS)以及通道中的分布偏移(CDS)来完成的,对其进行良好的初始化是有必要的,因为他会使网络最接近最佳值,只有在达到最大精度之前才进行微调。
KL-Divergence: 内核分布器移位后产生的VQK应该具有与原始权重类似的分布。量化过程仅适用缩放因子来评估VQK的整数值
最小化L2范数:初始化内核移位器张量的值,使得逐元素乘法后的结果尽可能接近原始值。
两种方法效果是一致的。
Optimized Inference首先将它乘以输入张量,而不是移动VQK,这意味着大部分操作将以整数值而不是浮点数计算,根据所使用的硬件,这可以通过8位操作实现2-10倍的加速。使硬件可以利用整数运算而不必使用浮点运算。
给定BLK的块大小,当chi是BLK的倍数时,该方法将执行比其原始对应物少的FP乘法的BLK倍。对于块大小为128,通过简单的将卷积层更改为DSConv,将显著减少2个量级的fp乘法
在执行给定内核中所有卷积的总和之后,将在稍微应用信道分布移位,进一步改善存储器和计算能力,如果模型在卷积运算符之后包括它,则可以将通道移位合并到BN中。
使用此过程,也可以很容易地计算方向传播。借助上图可以看出方向传播被简单地分解为三个简单的操作。还应该注意的是,VQK核是不可训练的,因此不需要计算∂wr/∂w的值。相反,只需要计算∂ξr/∂ξ,它的大小明显小于∂wr/∂w。
在掌纹ROI区域分割系统中,融合分布移位卷积(DSConv)YOLOv5扮演着关键角色。这一节将深入探讨DSConv在YOLOv5架构中的运用,其与传统方法的对比以及它所带来的优势。
DSConv在YOLOv5中的集成DSConv,即分布偏移卷积,是一种能够有效替代标准神经网络体系结构的卷积变体。其核心在于将传统卷积内核分解为两个要素:可变量化内核(VQK)和分布偏移。这一方法在保持与原始卷积相同输出的同时,极大地降低了存储器使用并提高了计算速度。在YOLOv5中,DSConv的应用为模型带来了显著的性能提升,特别是在掌纹ROI区域分割任务上。
DSConv与深度可分离卷积的对比DSConv作为深度可分离卷积的变体,与传统的深度可分离卷积有着一些本质上的差异。深度可分离卷积将标准卷积分解为深度卷积和逐点卷积两个步骤,这种方法在轻量级模型设计中非常受欢迎。然而,DSConv与深度可分离卷积相比,在模型性能和表现上有着更大的优势。
DSConv利用可学习的卷积核进一步提高了模型的表现。这个特性使得DSConv在卷积操作中拥有更多灵活性和表达能力,有助于模型更好地适应复杂的掌纹ROI区域分割任务。与此同时,DSConv通过整数值存储可变量化内核,在内存使用和计算速度上均有显著优势。这使得在保持高精度的同时,模型在计算效率上有了大幅提升。
DSConv在掌纹ROI区域分割任务中的优势掌纹ROI区域分割任务对模型的要求较高,需要高效的特征提取和准确的区域分割。DSConv在此任务中展现出了其独特的优势。其能够提供更精确的特征表达,帮助模型更好地理解掌纹的复杂纹路和结构。同时,DSConv所带来的存储器使用减少和计算速度提升,使得模型在实际应用中更具可行性和效率。
Yolov5-DSConv网络结构配置文件# YOLOv5 by Ultralytics, AGPL-3.0 license # Parameters nc: 1 # number of classes depth_multiple: 0.33 # model depth multiple width_multiple: 0.25 # layer channel multiple anchors: - [10,13, 16,30, 33,23] # P3/8 - [30,61, 62,45, 59,119] # P4/16 - [116,90, 156,198, 373,326] # P5/32 # YOLOv5 v6.0 backbone backbone: # [from, number, module, args] [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 [-1, 3, C3, [128]], [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 [-1, 6, C3, [256]], [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 [-1, 9, C3, [512]], [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 [-1, 3, C3, [1024]], [-1, 1, SPPF, [1024, 5]], # 9 ] # YOLOv5 v6.0 head head: [[-1, 1, Conv, [512, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 6], 1, Concat, [1]], # cat backbone P4 [-1, 3, C3_DSConv, [512, False]], # 13 [-1, 1, Conv, [256, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 4], 1, Concat, [1]], # cat backbone P3 [-1, 3, C3_DSConv, [256, False]], # 17 (P3/8-small) [-1, 1, Conv, [256, 3, 2]], [[-1, 14], 1, Concat, [1]], # cat head P4 [-1, 3, C3_DSConv, [512, False]], # 20 (P4/16-medium) [-1, 1, Conv, [512, 3, 2]], [[-1, 10], 1, Concat, [1]], # cat head P5 [-1, 3, C3_DSConv, [1024, False]], # 23 (P5/32-large) [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) ]
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849训练损失(train/box_loss, train/seg_loss, train/obj_loss, train/cls_loss):这些损失值表示模型在训练集上的表现,包括边界框损失、分割损失、对象损失和分类损失。
指标评价(metrics/ precision、metrics/recall、metrics/mAP_0.5、metrics/mAP_0.5:0.95):这些指标分别对应准确度、识别率和不同阈值下的平均精度。这些指标分别针对两个类别(B 和 M)进行了测量。
验证损失(val/box_loss, val/seg_loss, val/obj_loss, val/cls_loss):这些损失值表示模型在验证集上的表现。
学习率(x/lr0, x/lr1, x/lr2):这些值表示训练过程中的学习率。
训练结果可视化
可视化提供了模型在训练周期内各种指标的性能的全面概述。现在,让我们详细分析这些结果:
框、对象和类损失(训练):随着时代的进展,所有三个损失指标(框、对象和类)都显示出下降趋势。这表明该模型正在有效地学习,提高了其预测边界框、识别对象和正确分类的能力。
框、对象和类损失(验证):验证损失也会随着时间的推移而减少,反映了训练损失。这是一个积极的信号,表明该模型没有过度拟合,并且可以很好地推广到未见过的数据。
精确度和召回率:精确度一开始相当低,但后来显着增加,表明模型在做出正确的积极预测方面变得越来越好。召回率也增加,表明模型查找所有相关案例的能力不断提高。
mAP@0.5 和 mAP@0.5:0.95:两个 mAP 指标都会随着时间的推移而增加。mAP@0.5 始终高于 mAP@0.5:0.95,这是预期的,因为后者是一个更严格的指标。这些指标的不断改进表明该模型在准确定位和分类对象方面的熟练程度不断提高。
学习率 ( x/lr0、x/lr1和x/lr2) 似乎随着时间的推移而降低,这在训练深度学习模型中很常见。这种逐渐减少有助于微调模型的权重,特别是当它接近最佳性能时。
下图完整源码&数据集&环境部署视频教程&自定义UI界面
参考博客《融合分布移位卷积(DSConv)YOLO的掌纹ROI区域分割系统》
[1]刘明,李丽华,李哲.基于指导滤波与二值图像组互相关匹配的3D掌纹识别[J].计算机科学.2014,(9).DOI:10.11896/j.issn.1002-137X.2014.09.058 .
[2]赵志刚,吴鑫,洪丹枫,等.基于信息熵的GLBP掌纹识别算法[J].计算机科学.2014,(8).DOI:10.11896/j.issn.1002-137X.2014.08.062 .
[3]曹雏清,李瑞峰,赵立军.基于深度图像技术的手势识别方法[J].计算机工程.2012,(8).DOI:10.3969/j.issn.1000-3428.2012.08.006 .
[4]徐婉莹,黄新生,刘育浩,等.一种基于Gabor小波的局部特征尺度提取方法[J].中国图象图形学报A.2011,(1).
[5]苑玮琦,李燕.手形特征点定位方法[J].计算机应用.2010,(12).
[6]David Lowrence.Biometrics and retail: moving towards the future[J].Biometric Technology Today.2014,2014(2).7-9.
[7]Christian Rathgeb,Andreas Uhl.A survey on biometric cryptosystems and cancelable biometrics[J].EURASIP Journal on Information Security.2011.2011
[8]Zhang, D.,Lu, G.,Li, W.,等.Palmprint Recognition Using 3-D Information[J].IEEE transactions on systems, man and cybernetics, Part C. Applications and reviews: A publication of the IEEE Systems, Man, and Cybernetics Society.2009,39(5).505-519.
[9]Xu, Shuang,Suo, Jidong,Ding, Jifeng.Improved linear discriminant analysis based on two-dimensional Gabor for palmprint recognition[C].2011.
[10]Shuang Xu,Jidong Suo,Jiyin Zhao.Research on the Location and Segmentation Technologies in Palmprint Identification[C].2011.
相关知识
基于YOLO v7
基于改进YOLO v5s模型的水稻病虫害监测系统
基于改进YoloV3卷积神经网络的番茄病虫害检测
复杂场景下的复杂缺陷检测方法——深度学习算法综述(转载 https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news
基于卷积神经网络和集成学习的材质识别和分割方法研究
基于深度卷积神经网络的粮仓害虫监测系统实现
基于超轻量级全卷积神经网络的花卉识别方法
基于深度学习YOLOv8\YOLOv5的花卉识别鲜花识别检测分类系统设计
YOLO模型实现农作物病虫害虫识别带GUI界面
【图像分割】基于阈值法实现大脑图像分割附Matlab代码
网址: 融合分布移位卷积(DSConv)YOLO的掌纹ROI区域分割系统 https://m.huajiangbk.com/newsview1580777.html
上一篇: 论文解读——掌纹生成网络 RPG |
下一篇: 掌纹识别研究进展综述 |