Aidluxベースの駐車標識検出(coco 80クラスのターゲット検出に変更可能)

●プロジェクト名
Aidluxベースの駐車標識検出(coco 80クラスのターゲット検出に変更可能)

●プロジェクト紹介

このプロジェクトでは、駐車標識の検出を Aidlux にデプロイし、ソース コード上の coco 80 タイプのターゲット検出インデックスを変更して、他の 79 タイプのターゲットを直接検出できます。これを直接変更して、独自のプロジェクトにすぐに移植できます。

●期待される効果

このプロジェクトは、AidLux がインストールされた AidLux デバイス s855 を使用して実装されます。これにより、AidLux ユーザーはすぐに開始し、ディープラーニング検出の効果を体験し、開発の楽しみを増やすことができます。

エッジ コンピューティング デバイスの利点には主に次の側面が含まれます。
帯域幅の節約: エッジ コンピューティング デバイスはソースでデータを処理し、重要なデータのみを送信できるため、帯域幅を節約できます。
待ち時間の短縮: エッジ コンピューティング デバイスは待ち時間を短縮し、応答性を向上させることができます。
ネットワーク パフォーマンスの最適化: エッジ コンピューティング デバイスは、企業がデータをリアルタイムで分析および処理できるようにすることで、ネットワーク パフォーマンスを向上させます。
ローカライズされたサービスの提供: エッジ コンピューティング デバイスは、スマート シティ、自動運転車、医療業界などでローカライズされたサービスを提供でき、エッジ コンピューティング デバイスはより高速で正確なサービスを提供できます。
セキュリティの向上: エッジ コンピューティング デバイスは、データ送信中のセキュリティ リスクを軽減し、セキュリティを向上させることができます。

ほとんどの企業にとって、coco の 80 カテゴリは、ほとんどのシナリオの事前調査とシミュレーションをサポートできます。このプロジェクトでは、yolov5 を AidLux に移植し、ソース コード内のカテゴリを直接変更して、80 カテゴリのいずれかの検出を実現できます。

開発者にとって、AI プロジェクトにおけるさまざまなアルゴリズムのデータセットの準備 + モデルのトレーニング + モデルのデプロイは依然としてかなりの困難を伴います。AidLux の出現により、Android デバイスを、非仮想形式で Android と Linux の両方のシステム環境を備えたエッジ コンピューティング デバイスに変えることができます。主流の AI フレームワークをサポートし、展開が非常に簡単で、コンピューティング リソースをスケジュールするための専用インターフェイスを備えているため、AI アプリケーションの敷居が大幅に下がります。

Aidluxの紹介

Aidlux は、ARM アーキテクチャに基づいた、クロスエコロジー (Android/Hongmeng+Linux) のワンストップ AIoT アプリケーション開発および展開プラットフォームであり、Android 携帯電話で使用できます (ハードウェアおよびソフトウェアの最小構成要件: ①Android システム バージョン >= 6、②残りのストレージ容量 > 650MB (AidLux1.1)、③CPU は arm64-v8a アーキテクチャをサポート
ここに画像の説明を挿入
)

同社がリリースしたエッジ コンピューティング デバイス上で実行および開発

以下はソースコードです。

yolov5.py

# aidlux相关
from cvs import *
import aidlite_gpu
from utils import detect_postprocess, preprocess_img, draw_detect_res

import cv2



# 加载模型
model_path = 'yolov5s-fp16.tflite'
# 定义输入输出shape
in_shape = [1 * 640 * 640 * 3 * 4]
out_shape = [1 * 25200 * 85 * 4, 1 * 3 * 80 * 80 * 85 * 4, 1 * 3 * 40 * 40 * 85 * 4, 1 * 3 * 20 * 20 * 85 * 4]

# 载入模型
aidlite = aidlite_gpu.aidlite()
# 载入yolov5检测模型
aidlite.ANNModel(model_path, in_shape, out_shape, 4, 0)

# cap = cvs.VideoCapture(0)
sign = 11


frame=cvs.imread("5.jpg",1)
# 预处理
img = preprocess_img(frame, target_shape=(640, 640), div_num=255, means=None, stds=None)

aidlite.setInput_Float32(img, 640, 640)
# 推理
aidlite.invoke()
pred = aidlite.getOutput_Float32(0)
pred = pred.reshape(1, 25200, 85)[0]
pred = detect_postprocess(pred, frame.shape, [640, 640, 3], conf_thres=0.5, iou_thres=0.45)
res_img, detec_taget = draw_detect_res(frame, pred, sign)

if detec_taget == 1:
    cv2.imwrite("detect_image.jpg", res_img)
    cvs.imshow(res_img)
其中,经过请教rockey老师和江大白老师得知,cvs是将opencv在aidlux做了兼容性开发,语法同opencv,也这里是读取图片的方式,也可用读取摄像头的方式。我们检测stop sign图片,在coco 80类目标检测中索引为11(从0开始),所以设置sign=0,。如果是行人或者其他,直接修改sign的值为对应索引即可。


相关后处理函数utils.py

```python
import cv2
import numpy as np

coco_class = ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',
        'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
        'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
        'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
        'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
        'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
        'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone',
        'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear',
        'hair drier', 'toothbrush']

def xywh2xyxy(x):
    '''
    Box (center x, center y, width, height) to (x1, y1, x2, y2)
    '''
    y = np.copy(x)
    y[:, 0] = x[:, 0] - x[:, 2] / 2  # top left x
    y[:, 1] = x[:, 1] - x[:, 3] / 2  # top left y
    y[:, 2] = x[:, 0] + x[:, 2] / 2  # bottom right x
    y[:, 3] = x[:, 1] + x[:, 3] / 2  # bottom right y
    return y

def xyxy2xywh(box):
    '''
    Box (left_top x, left_top y, right_bottom x, right_bottom y) to (left_top x, left_top y, width, height)
    '''
    box[:, 2:] = box[:, 2:] - box[:, :2]
    return box

def NMS(dets, thresh):
    '''
    单类NMS算法
    dets.shape = (N, 5), (left_top x, left_top y, right_bottom x, right_bottom y, Scores)
    '''
    x1 = dets[:,0]
    y1 = dets[:,1]
    x2 = dets[:,2]
    y2 = dets[:,3]
    areas = (y2-y1+1) * (x2-x1+1)
    scores = dets[:,4]
    keep = []
    index = scores.argsort()[::-1]
    while index.size >0:
        i = index[0]       # every time the first is the biggst, and add it directly
        keep.append(i)
        x11 = np.maximum(x1[i], x1[index[1:]])    # calculate the points of overlap 
        y11 = np.maximum(y1[i], y1[index[1:]])
        x22 = np.minimum(x2[i], x2[index[1:]])
        y22 = np.minimum(y2[i], y2[index[1:]])
        w = np.maximum(0, x22-x11+1)    # the weights of overlap
        h = np.maximum(0, y22-y11+1)    # the height of overlap
        overlaps = w*h
        ious = overlaps / (areas[i]+areas[index[1:]] - overlaps)
        idx = np.where(ious<=thresh)[0]
        index = index[idx+1]   # because index start from 1
 
    return dets[keep]

def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):
    # Resize and pad image while meeting stride-multiple constraints
    shape = img.shape[:2]  # current shape [height, width]
    if isinstance(new_shape, int):
        new_shape = (new_shape, new_shape)

    # Scale ratio (new / old)
    r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
    if not scaleup:  # only scale down, do not scale up (for better test mAP)
        r = min(r, 1.0)

    # Compute padding
    ratio = r, r  # width, height ratios
    new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
    dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # wh padding
    if auto:  # minimum rectangle
        dw, dh = np.mod(dw, stride), np.mod(dh, stride)  # wh padding
    elif scaleFill:  # stretch
        dw, dh = 0.0, 0.0
        new_unpad = (new_shape[1], new_shape[0])
        ratio = new_shape[1] / shape[1], new_shape[0] / shape[0]  # width, height ratios

    dw /= 2  # divide padding into 2 sides
    dh /= 2

    if shape[::-1] != new_unpad:  # resize
        img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
    top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
    left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
    img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border
    return img, ratio, (dw, dh)

def preprocess_img(img, target_shape:tuple=None, div_num=255, means:list=[0.485, 0.456, 0.406], stds:list=[0.229, 0.224, 0.225]):
    '''
    图像预处理:
    target_shape: 目标shape
    div_num: 归一化除数
    means: len(means)==图像通道数,通道均值, None不进行zscore
    stds: len(stds)==图像通道数,通道方差, None不进行zscore
    '''
    img_processed = np.copy(img)
    # resize
    if target_shape:
        # img_processed = cv2.resize(img_processed, target_shape)
        img_processed = letterbox(img_processed, target_shape, stride=None, auto=False)[0]

    img_processed = img_processed.astype(np.float32)
    img_processed = img_processed/div_num

    # z-score
    if means is not None and stds is not None:
        means = np.array(means).reshape(1, 1, -1)
        stds = np.array(stds).reshape(1, 1, -1)
        img_processed = (img_processed-means)/stds

    # unsqueeze
    img_processed = img_processed[None, :]

    return img_processed.astype(np.float32)
    
def convert_shape(shapes:tuple or list, int8=False):
    '''
    转化为aidlite需要的格式
    '''
    if isinstance(shapes, tuple):
        shapes = [shapes]
    out = []
    for shape in shapes:
        nums = 1 if int8 else 4
        for n in shape:
            nums *= n
        out.append(nums)
    return out

def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None):
    # Rescale coords (xyxy) from img1_shape to img0_shape
    if ratio_pad is None:  # calculate from img0_shape
        gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1])  # gain  = old / new
        pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2  # wh padding
    else:
        gain = ratio_pad[0][0]
        pad = ratio_pad[1]

    coords[:, [0, 2]] -= pad[0]  # x padding
    coords[:, [1, 3]] -= pad[1]  # y padding
    coords[:, :4] /= gain
    clip_coords(coords, img0_shape)
    return coords


def clip_coords(boxes, img_shape):
    # Clip bounding xyxy bounding boxes to image shape (height, width)
    boxes[:, 0].clip(0, img_shape[1], out=boxes[:, 0])  # x1
    boxes[:, 1].clip(0, img_shape[0], out=boxes[:, 1])  # y1
    boxes[:, 2].clip(0, img_shape[1], out=boxes[:, 2])  # x2
    boxes[:, 3].clip(0, img_shape[0], out=boxes[:, 3])  # y2

def detect_postprocess(prediction, img0shape, img1shape, conf_thres=0.25, iou_thres=0.45):
    '''
    检测输出后处理
    prediction: aidlite模型预测输出
    img0shape: 原始图片shape
    img1shape: 输入图片shape
    conf_thres: 置信度阈值
    iou_thres: IOU阈值
    return: list[np.ndarray(N, 5)], 对应类别的坐标框信息, xywh、conf
    '''
    h, w, _ = img1shape
    cls_num = prediction.shape[-1] - 5
    valid_condidates = prediction[prediction[..., 4] > conf_thres]
    valid_condidates[:, 0] *= w
    valid_condidates[:, 1] *= h
    valid_condidates[:, 2] *= w
    valid_condidates[:, 3] *= h
    valid_condidates[:, :4] = xywh2xyxy(valid_condidates[:, :4])
    valid_condidates = valid_condidates[(valid_condidates[:, 0] > 0) & (valid_condidates[:, 1] > 0) & (valid_condidates[:, 2] > 0) & (valid_condidates[:, 3] > 0)]
    box_cls = valid_condidates[:, 5:].argmax(1)
    cls_box = []
    for i in range(cls_num):
        temp_boxes = valid_condidates[box_cls == i]
        if(len(temp_boxes) == 0):
            cls_box.append([])
            continue
        temp_boxes = NMS(temp_boxes, iou_thres)
        temp_boxes[:, :4] = scale_coords([h, w], temp_boxes[:, :4] , img0shape).round()
        temp_boxes[:, :4] = xyxy2xywh(temp_boxes[:, :4])
        cls_box.append(temp_boxes[:, :5])
    return cls_box

def draw_detect_res(img, all_boxes,sign):
    '''
    检测结果绘制
    '''
    img = img.astype(np.uint8)
    color_step = int(255/len(all_boxes))
    for bi in range(len(all_boxes)):
        if len(all_boxes[bi]) == 0:
            continue
        for box in all_boxes[bi]:
            x, y, w, h = [int(t) for t in box[:4]]
            if bi != sign:
                continue
            if bi == sign:
                type = 1
            cv2.putText(img, f'{coco_class[bi]}', (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
            cv2.rectangle(img, (x,y), (x+w, y+h),(0, bi*color_step, 255-bi*color_step),thickness = 2)
    return img,type

すべてのファイル (テスト イメージ、Python ファイル、yolov5s 推論モデル)

ここに画像の説明を挿入

●操作手順

Aidlux をリモートでリンクし、ホーム フォルダーを見つけて、上記のファイルを含むフォルダーをアップロードします。

ここに画像の説明を挿入
ここに画像の説明を挿入

ここに画像の説明を挿入
ダブルクリックして yolov5.py を開き、Bulid をクリックして「
ここに画像の説明を挿入
今すぐ実行」を見つけて「実行」をクリックします。
ここに画像の説明を挿入
操作の結果:
ここに画像の説明を挿入
ソース コードはここにあります。


おすすめ

転載: blog.csdn.net/qq_43207709/article/details/131187122