diff options
Diffstat (limited to 'r_facelib/detection/yolov5face/models')
| -rw-r--r-- | r_facelib/detection/yolov5face/models/__init__.py | 0 | ||||
| -rw-r--r-- | r_facelib/detection/yolov5face/models/common.py | 299 | ||||
| -rw-r--r-- | r_facelib/detection/yolov5face/models/experimental.py | 45 | ||||
| -rw-r--r-- | r_facelib/detection/yolov5face/models/yolo.py | 235 | ||||
| -rw-r--r-- | r_facelib/detection/yolov5face/models/yolov5l.yaml | 47 | ||||
| -rw-r--r-- | r_facelib/detection/yolov5face/models/yolov5n.yaml | 45 | 
6 files changed, 671 insertions, 0 deletions
| diff --git a/r_facelib/detection/yolov5face/models/__init__.py b/r_facelib/detection/yolov5face/models/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/r_facelib/detection/yolov5face/models/__init__.py diff --git a/r_facelib/detection/yolov5face/models/common.py b/r_facelib/detection/yolov5face/models/common.py new file mode 100644 index 0000000..96894d5 --- /dev/null +++ b/r_facelib/detection/yolov5face/models/common.py @@ -0,0 +1,299 @@ +# This file contains modules common to various models
 +
 +import math
 +
 +import numpy as np
 +import torch
 +from torch import nn
 +
 +from r_facelib.detection.yolov5face.utils.datasets import letterbox
 +from r_facelib.detection.yolov5face.utils.general import (
 +    make_divisible,
 +    non_max_suppression,
 +    scale_coords,
 +    xyxy2xywh,
 +)
 +
 +
 +def autopad(k, p=None):  # kernel, padding
 +    # Pad to 'same'
 +    if p is None:
 +        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # auto-pad
 +    return p
 +
 +
 +def channel_shuffle(x, groups):
 +    batchsize, num_channels, height, width = x.data.size()
 +    channels_per_group = torch.div(num_channels, groups, rounding_mode="trunc")
 +
 +    # reshape
 +    x = x.view(batchsize, groups, channels_per_group, height, width)
 +    x = torch.transpose(x, 1, 2).contiguous()
 +
 +    # flatten
 +    return x.view(batchsize, -1, height, width)
 +
 +
 +def DWConv(c1, c2, k=1, s=1, act=True):
 +    # Depthwise convolution
 +    return Conv(c1, c2, k, s, g=math.gcd(c1, c2), act=act)
 +
 +
 +class Conv(nn.Module):
 +    # Standard convolution
 +    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
 +        super().__init__()
 +        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
 +        self.bn = nn.BatchNorm2d(c2)
 +        self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
 +
 +    def forward(self, x):
 +        return self.act(self.bn(self.conv(x)))
 +
 +    def fuseforward(self, x):
 +        return self.act(self.conv(x))
 +
 +
 +class StemBlock(nn.Module):
 +    def __init__(self, c1, c2, k=3, s=2, p=None, g=1, act=True):
 +        super().__init__()
 +        self.stem_1 = Conv(c1, c2, k, s, p, g, act)
 +        self.stem_2a = Conv(c2, c2 // 2, 1, 1, 0)
 +        self.stem_2b = Conv(c2 // 2, c2, 3, 2, 1)
 +        self.stem_2p = nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True)
 +        self.stem_3 = Conv(c2 * 2, c2, 1, 1, 0)
 +
 +    def forward(self, x):
 +        stem_1_out = self.stem_1(x)
 +        stem_2a_out = self.stem_2a(stem_1_out)
 +        stem_2b_out = self.stem_2b(stem_2a_out)
 +        stem_2p_out = self.stem_2p(stem_1_out)
 +        return self.stem_3(torch.cat((stem_2b_out, stem_2p_out), 1))
 +
 +
 +class Bottleneck(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 = Conv(c1, c_, 1, 1)
 +        self.cv2 = Conv(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 BottleneckCSP(nn.Module):
 +    # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
 +    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
 +        super().__init__()
 +        c_ = int(c2 * e)  # hidden channels
 +        self.cv1 = Conv(c1, c_, 1, 1)
 +        self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False)
 +        self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False)
 +        self.cv4 = Conv(2 * c_, c2, 1, 1)
 +        self.bn = nn.BatchNorm2d(2 * c_)  # applied to cat(cv2, cv3)
 +        self.act = nn.LeakyReLU(0.1, inplace=True)
 +        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))
 +
 +    def forward(self, x):
 +        y1 = self.cv3(self.m(self.cv1(x)))
 +        y2 = self.cv2(x)
 +        return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1))))
 +
 +
 +class C3(nn.Module):
 +    # CSP Bottleneck with 3 convolutions
 +    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
 +        super().__init__()
 +        c_ = int(c2 * e)  # hidden channels
 +        self.cv1 = Conv(c1, c_, 1, 1)
 +        self.cv2 = Conv(c1, c_, 1, 1)
 +        self.cv3 = Conv(2 * c_, c2, 1)  # act=FReLU(c2)
 +        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))
 +
 +    def forward(self, x):
 +        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))
 +
 +
 +class ShuffleV2Block(nn.Module):
 +    def __init__(self, inp, oup, stride):
 +        super().__init__()
 +
 +        if not 1 <= stride <= 3:
 +            raise ValueError("illegal stride value")
 +        self.stride = stride
 +
 +        branch_features = oup // 2
 +
 +        if self.stride > 1:
 +            self.branch1 = nn.Sequential(
 +                self.depthwise_conv(inp, inp, kernel_size=3, stride=self.stride, padding=1),
 +                nn.BatchNorm2d(inp),
 +                nn.Conv2d(inp, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
 +                nn.BatchNorm2d(branch_features),
 +                nn.SiLU(),
 +            )
 +        else:
 +            self.branch1 = nn.Sequential()
 +
 +        self.branch2 = nn.Sequential(
 +            nn.Conv2d(
 +                inp if (self.stride > 1) else branch_features,
 +                branch_features,
 +                kernel_size=1,
 +                stride=1,
 +                padding=0,
 +                bias=False,
 +            ),
 +            nn.BatchNorm2d(branch_features),
 +            nn.SiLU(),
 +            self.depthwise_conv(branch_features, branch_features, kernel_size=3, stride=self.stride, padding=1),
 +            nn.BatchNorm2d(branch_features),
 +            nn.Conv2d(branch_features, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
 +            nn.BatchNorm2d(branch_features),
 +            nn.SiLU(),
 +        )
 +
 +    @staticmethod
 +    def depthwise_conv(i, o, kernel_size, stride=1, padding=0, bias=False):
 +        return nn.Conv2d(i, o, kernel_size, stride, padding, bias=bias, groups=i)
 +
 +    def forward(self, x):
 +        if self.stride == 1:
 +            x1, x2 = x.chunk(2, dim=1)
 +            out = torch.cat((x1, self.branch2(x2)), dim=1)
 +        else:
 +            out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)
 +        out = channel_shuffle(out, 2)
 +        return out
 +
 +
 +class SPP(nn.Module):
 +    # Spatial pyramid pooling layer used in YOLOv3-SPP
 +    def __init__(self, c1, c2, k=(5, 9, 13)):
 +        super().__init__()
 +        c_ = c1 // 2  # hidden channels
 +        self.cv1 = Conv(c1, c_, 1, 1)
 +        self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
 +        self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])
 +
 +    def forward(self, x):
 +        x = self.cv1(x)
 +        return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))
 +
 +
 +class Focus(nn.Module):
 +    # Focus wh information into c-space
 +    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
 +        super().__init__()
 +        self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
 +
 +    def forward(self, x):  # x(b,c,w,h) -> y(b,4c,w/2,h/2)
 +        return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
 +
 +
 +class Concat(nn.Module):
 +    # Concatenate a list of tensors along dimension
 +    def __init__(self, dimension=1):
 +        super().__init__()
 +        self.d = dimension
 +
 +    def forward(self, x):
 +        return torch.cat(x, self.d)
 +
 +
 +class NMS(nn.Module):
 +    # Non-Maximum Suppression (NMS) module
 +    conf = 0.25  # confidence threshold
 +    iou = 0.45  # IoU threshold
 +    classes = None  # (optional list) filter by class
 +
 +    def forward(self, x):
 +        return non_max_suppression(x[0], conf_thres=self.conf, iou_thres=self.iou, classes=self.classes)
 +
 +
 +class AutoShape(nn.Module):
 +    # input-robust model wrapper for passing cv2/np/PIL/torch inputs. Includes preprocessing, inference and NMS
 +    img_size = 640  # inference size (pixels)
 +    conf = 0.25  # NMS confidence threshold
 +    iou = 0.45  # NMS IoU threshold
 +    classes = None  # (optional list) filter by class
 +
 +    def __init__(self, model):
 +        super().__init__()
 +        self.model = model.eval()
 +
 +    def autoshape(self):
 +        print("autoShape already enabled, skipping... ")  # model already converted to model.autoshape()
 +        return self
 +
 +    def forward(self, imgs, size=640, augment=False, profile=False):
 +        # Inference from various sources. For height=720, width=1280, RGB images example inputs are:
 +        #   OpenCV:          = cv2.imread('image.jpg')[:,:,::-1]  # HWC BGR to RGB x(720,1280,3)
 +        #   PIL:             = Image.open('image.jpg')  # HWC x(720,1280,3)
 +        #   numpy:           = np.zeros((720,1280,3))  # HWC
 +        #   torch:           = torch.zeros(16,3,720,1280)  # BCHW
 +        #   multiple:        = [Image.open('image1.jpg'), Image.open('image2.jpg'), ...]  # list of images
 +
 +        p = next(self.model.parameters())  # for device and type
 +        if isinstance(imgs, torch.Tensor):  # torch
 +            return self.model(imgs.to(p.device).type_as(p), augment, profile)  # inference
 +
 +        # Pre-process
 +        n, imgs = (len(imgs), imgs) if isinstance(imgs, list) else (1, [imgs])  # number of images, list of images
 +        shape0, shape1 = [], []  # image and inference shapes
 +        for i, im in enumerate(imgs):
 +            im = np.array(im)  # to numpy
 +            if im.shape[0] < 5:  # image in CHW
 +                im = im.transpose((1, 2, 0))  # reverse dataloader .transpose(2, 0, 1)
 +            im = im[:, :, :3] if im.ndim == 3 else np.tile(im[:, :, None], 3)  # enforce 3ch input
 +            s = im.shape[:2]  # HWC
 +            shape0.append(s)  # image shape
 +            g = size / max(s)  # gain
 +            shape1.append([y * g for y in s])
 +            imgs[i] = im  # update
 +        shape1 = [make_divisible(x, int(self.stride.max())) for x in np.stack(shape1, 0).max(0)]  # inference shape
 +        x = [letterbox(im, new_shape=shape1, auto=False)[0] for im in imgs]  # pad
 +        x = np.stack(x, 0) if n > 1 else x[0][None]  # stack
 +        x = np.ascontiguousarray(x.transpose((0, 3, 1, 2)))  # BHWC to BCHW
 +        x = torch.from_numpy(x).to(p.device).type_as(p) / 255.0  # uint8 to fp16/32
 +
 +        # Inference
 +        with torch.no_grad():
 +            y = self.model(x, augment, profile)[0]  # forward
 +        y = non_max_suppression(y, conf_thres=self.conf, iou_thres=self.iou, classes=self.classes)  # NMS
 +
 +        # Post-process
 +        for i in range(n):
 +            scale_coords(shape1, y[i][:, :4], shape0[i])
 +
 +        return Detections(imgs, y, self.names)
 +
 +
 +class Detections:
 +    # detections class for YOLOv5 inference results
 +    def __init__(self, imgs, pred, names=None):
 +        super().__init__()
 +        d = pred[0].device  # device
 +        gn = [torch.tensor([*(im.shape[i] for i in [1, 0, 1, 0]), 1.0, 1.0], device=d) for im in imgs]  # normalizations
 +        self.imgs = imgs  # list of images as numpy arrays
 +        self.pred = pred  # list of tensors pred[0] = (xyxy, conf, cls)
 +        self.names = names  # class names
 +        self.xyxy = pred  # xyxy pixels
 +        self.xywh = [xyxy2xywh(x) for x in pred]  # xywh pixels
 +        self.xyxyn = [x / g for x, g in zip(self.xyxy, gn)]  # xyxy normalized
 +        self.xywhn = [x / g for x, g in zip(self.xywh, gn)]  # xywh normalized
 +        self.n = len(self.pred)
 +
 +    def __len__(self):
 +        return self.n
 +
 +    def tolist(self):
 +        # return a list of Detections objects, i.e. 'for result in results.tolist():'
 +        x = [Detections([self.imgs[i]], [self.pred[i]], self.names) for i in range(self.n)]
 +        for d in x:
 +            for k in ["imgs", "pred", "xyxy", "xyxyn", "xywh", "xywhn"]:
 +                setattr(d, k, getattr(d, k)[0])  # pop out of list
 +        return x
 diff --git a/r_facelib/detection/yolov5face/models/experimental.py b/r_facelib/detection/yolov5face/models/experimental.py new file mode 100644 index 0000000..bdf7aea --- /dev/null +++ b/r_facelib/detection/yolov5face/models/experimental.py @@ -0,0 +1,45 @@ +# # This file contains experimental modules
 +
 +import numpy as np
 +import torch
 +from torch import nn
 +
 +from r_facelib.detection.yolov5face.models.common import Conv
 +
 +
 +class CrossConv(nn.Module):
 +    # Cross Convolution Downsample
 +    def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False):
 +        # ch_in, ch_out, kernel, stride, groups, expansion, shortcut
 +        super().__init__()
 +        c_ = int(c2 * e)  # hidden channels
 +        self.cv1 = Conv(c1, c_, (1, k), (1, s))
 +        self.cv2 = Conv(c_, c2, (k, 1), (s, 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 MixConv2d(nn.Module):
 +    # Mixed Depthwise Conv https://arxiv.org/abs/1907.09595
 +    def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True):
 +        super().__init__()
 +        groups = len(k)
 +        if equal_ch:  # equal c_ per group
 +            i = torch.linspace(0, groups - 1e-6, c2).floor()  # c2 indices
 +            c_ = [(i == g).sum() for g in range(groups)]  # intermediate channels
 +        else:  # equal weight.numel() per group
 +            b = [c2] + [0] * groups
 +            a = np.eye(groups + 1, groups, k=-1)
 +            a -= np.roll(a, 1, axis=1)
 +            a *= np.array(k) ** 2
 +            a[0] = 1
 +            c_ = np.linalg.lstsq(a, b, rcond=None)[0].round()  # solve for equal weight indices, ax = b
 +
 +        self.m = nn.ModuleList([nn.Conv2d(c1, int(c_[g]), k[g], s, k[g] // 2, bias=False) for g in range(groups)])
 +        self.bn = nn.BatchNorm2d(c2)
 +        self.act = nn.LeakyReLU(0.1, inplace=True)
 +
 +    def forward(self, x):
 +        return x + self.act(self.bn(torch.cat([m(x) for m in self.m], 1)))
 diff --git a/r_facelib/detection/yolov5face/models/yolo.py b/r_facelib/detection/yolov5face/models/yolo.py new file mode 100644 index 0000000..02479dc --- /dev/null +++ b/r_facelib/detection/yolov5face/models/yolo.py @@ -0,0 +1,235 @@ +import math
 +from copy import deepcopy
 +from pathlib import Path
 +
 +import torch
 +import yaml  # for torch hub
 +from torch import nn
 +
 +from r_facelib.detection.yolov5face.models.common import (
 +    C3,
 +    NMS,
 +    SPP,
 +    AutoShape,
 +    Bottleneck,
 +    BottleneckCSP,
 +    Concat,
 +    Conv,
 +    DWConv,
 +    Focus,
 +    ShuffleV2Block,
 +    StemBlock,
 +)
 +from r_facelib.detection.yolov5face.models.experimental import CrossConv, MixConv2d
 +from r_facelib.detection.yolov5face.utils.autoanchor import check_anchor_order
 +from r_facelib.detection.yolov5face.utils.general import make_divisible
 +from r_facelib.detection.yolov5face.utils.torch_utils import copy_attr, fuse_conv_and_bn
 +
 +
 +class Detect(nn.Module):
 +    stride = None  # strides computed during build
 +    export = False  # onnx export
 +
 +    def __init__(self, nc=80, anchors=(), ch=()):  # detection layer
 +        super().__init__()
 +        self.nc = nc  # number of classes
 +        self.no = nc + 5 + 10  # number of outputs per anchor
 +
 +        self.nl = len(anchors)  # number of detection layers
 +        self.na = len(anchors[0]) // 2  # number of anchors
 +        self.grid = [torch.zeros(1)] * self.nl  # init grid
 +        a = torch.tensor(anchors).float().view(self.nl, -1, 2)
 +        self.register_buffer("anchors", a)  # shape(nl,na,2)
 +        self.register_buffer("anchor_grid", a.clone().view(self.nl, 1, -1, 1, 1, 2))  # shape(nl,1,na,1,1,2)
 +        self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch)  # output conv
 +
 +    def forward(self, x):
 +        z = []  # inference output
 +        if self.export:
 +            for i in range(self.nl):
 +                x[i] = self.m[i](x[i])
 +            return x
 +        for i in range(self.nl):
 +            x[i] = self.m[i](x[i])  # conv
 +            bs, _, ny, nx = x[i].shape  # x(bs,255,20,20) to x(bs,3,20,20,85)
 +            x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
 +
 +            if not self.training:  # inference
 +                if self.grid[i].shape[2:4] != x[i].shape[2:4]:
 +                    self.grid[i] = self._make_grid(nx, ny).to(x[i].device)
 +
 +                y = torch.full_like(x[i], 0)
 +                y[..., [0, 1, 2, 3, 4, 15]] = x[i][..., [0, 1, 2, 3, 4, 15]].sigmoid()
 +                y[..., 5:15] = x[i][..., 5:15]
 +
 +                y[..., 0:2] = (y[..., 0:2] * 2.0 - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i]  # xy
 +                y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # wh
 +
 +                y[..., 5:7] = (
 +                    y[..., 5:7] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i]
 +                )  # landmark x1 y1
 +                y[..., 7:9] = (
 +                    y[..., 7:9] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i]
 +                )  # landmark x2 y2
 +                y[..., 9:11] = (
 +                    y[..., 9:11] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i]
 +                )  # landmark x3 y3
 +                y[..., 11:13] = (
 +                    y[..., 11:13] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i]
 +                )  # landmark x4 y4
 +                y[..., 13:15] = (
 +                    y[..., 13:15] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i]
 +                )  # landmark x5 y5
 +
 +                z.append(y.view(bs, -1, self.no))
 +
 +        return x if self.training else (torch.cat(z, 1), x)
 +
 +    @staticmethod
 +    def _make_grid(nx=20, ny=20):
 +        # yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)], indexing="ij") # for pytorch>=1.10
 +        yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)])
 +        return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float()
 +
 +
 +class Model(nn.Module):
 +    def __init__(self, cfg="yolov5s.yaml", ch=3, nc=None):  # model, input channels, number of classes
 +        super().__init__()
 +        self.yaml_file = Path(cfg).name
 +        with Path(cfg).open(encoding="utf8") as f:
 +            self.yaml = yaml.safe_load(f)  # model dict
 +
 +        # Define model
 +        ch = self.yaml["ch"] = self.yaml.get("ch", ch)  # input channels
 +        if nc and nc != self.yaml["nc"]:
 +            self.yaml["nc"] = nc  # override yaml value
 +
 +        self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch])  # model, savelist
 +        self.names = [str(i) for i in range(self.yaml["nc"])]  # default names
 +
 +        # Build strides, anchors
 +        m = self.model[-1]  # Detect()
 +        if isinstance(m, Detect):
 +            s = 128  # 2x min stride
 +            m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))])  # forward
 +            m.anchors /= m.stride.view(-1, 1, 1)
 +            check_anchor_order(m)
 +            self.stride = m.stride
 +            self._initialize_biases()  # only run once
 +
 +    def forward(self, x):
 +        return self.forward_once(x)  # single-scale inference, train
 +
 +    def forward_once(self, x):
 +        y = []  # outputs
 +        for m in self.model:
 +            if m.f != -1:  # if not from previous layer
 +                x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f]  # from earlier layers
 +
 +            x = m(x)  # run
 +            y.append(x if m.i in self.save else None)  # save output
 +
 +        return x
 +
 +    def _initialize_biases(self, cf=None):  # initialize biases into Detect(), cf is class frequency
 +        # https://arxiv.org/abs/1708.02002 section 3.3
 +        m = self.model[-1]  # Detect() module
 +        for mi, s in zip(m.m, m.stride):  # from
 +            b = mi.bias.view(m.na, -1)  # conv.bias(255) to (3,85)
 +            b.data[:, 4] += math.log(8 / (640 / s) ** 2)  # obj (8 objects per 640 image)
 +            b.data[:, 5:] += math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum())  # cls
 +            mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True)
 +
 +    def _print_biases(self):
 +        m = self.model[-1]  # Detect() module
 +        for mi in m.m:  # from
 +            b = mi.bias.detach().view(m.na, -1).T  # conv.bias(255) to (3,85)
 +            print(("%6g Conv2d.bias:" + "%10.3g" * 6) % (mi.weight.shape[1], *b[:5].mean(1).tolist(), b[5:].mean()))
 +
 +    def fuse(self):  # fuse model Conv2d() + BatchNorm2d() layers
 +        print("Fusing layers... ")
 +        for m in self.model.modules():
 +            if isinstance(m, Conv) and hasattr(m, "bn"):
 +                m.conv = fuse_conv_and_bn(m.conv, m.bn)  # update conv
 +                delattr(m, "bn")  # remove batchnorm
 +                m.forward = m.fuseforward  # update forward
 +            elif type(m) is nn.Upsample:
 +                m.recompute_scale_factor = None  # torch 1.11.0 compatibility
 +        return self
 +
 +    def nms(self, mode=True):  # add or remove NMS module
 +        present = isinstance(self.model[-1], NMS)  # last layer is NMS
 +        if mode and not present:
 +            print("Adding NMS... ")
 +            m = NMS()  # module
 +            m.f = -1  # from
 +            m.i = self.model[-1].i + 1  # index
 +            self.model.add_module(name=str(m.i), module=m)  # add
 +            self.eval()
 +        elif not mode and present:
 +            print("Removing NMS... ")
 +            self.model = self.model[:-1]  # remove
 +        return self
 +
 +    def autoshape(self):  # add autoShape module
 +        print("Adding autoShape... ")
 +        m = AutoShape(self)  # wrap model
 +        copy_attr(m, self, include=("yaml", "nc", "hyp", "names", "stride"), exclude=())  # copy attributes
 +        return m
 +
 +
 +def parse_model(d, ch):  # model_dict, input_channels(3)
 +    anchors, nc, gd, gw = d["anchors"], d["nc"], d["depth_multiple"], d["width_multiple"]
 +    na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors  # number of anchors
 +    no = na * (nc + 5)  # number of outputs = anchors * (classes + 5)
 +
 +    layers, save, c2 = [], [], ch[-1]  # layers, savelist, ch out
 +    for i, (f, n, m, args) in enumerate(d["backbone"] + d["head"]):  # from, number, module, args
 +        m = eval(m) if isinstance(m, str) else m  # eval strings
 +        for j, a in enumerate(args):
 +            try:
 +                args[j] = eval(a) if isinstance(a, str) else a  # eval strings
 +            except:
 +                pass
 +
 +        n = max(round(n * gd), 1) if n > 1 else n  # depth gain
 +        if m in [
 +            Conv,
 +            Bottleneck,
 +            SPP,
 +            DWConv,
 +            MixConv2d,
 +            Focus,
 +            CrossConv,
 +            BottleneckCSP,
 +            C3,
 +            ShuffleV2Block,
 +            StemBlock,
 +        ]:
 +            c1, c2 = ch[f], args[0]
 +
 +            c2 = make_divisible(c2 * gw, 8) if c2 != no else c2
 +
 +            args = [c1, c2, *args[1:]]
 +            if m in [BottleneckCSP, C3]:
 +                args.insert(2, n)
 +                n = 1
 +        elif m is nn.BatchNorm2d:
 +            args = [ch[f]]
 +        elif m is Concat:
 +            c2 = sum(ch[-1 if x == -1 else x + 1] for x in f)
 +        elif m is Detect:
 +            args.append([ch[x + 1] for x in f])
 +            if isinstance(args[1], int):  # number of anchors
 +                args[1] = [list(range(args[1] * 2))] * len(f)
 +        else:
 +            c2 = ch[f]
 +
 +        m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)  # module
 +        t = str(m)[8:-2].replace("__main__.", "")  # module type
 +        np = sum(x.numel() for x in m_.parameters())  # number params
 +        m_.i, m_.f, m_.type, m_.np = i, f, t, np  # attach index, 'from' index, type, number params
 +        save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1)  # append to savelist
 +        layers.append(m_)
 +        ch.append(c2)
 +    return nn.Sequential(*layers), sorted(save)
 diff --git a/r_facelib/detection/yolov5face/models/yolov5l.yaml b/r_facelib/detection/yolov5face/models/yolov5l.yaml new file mode 100644 index 0000000..98a9e2c --- /dev/null +++ b/r_facelib/detection/yolov5face/models/yolov5l.yaml @@ -0,0 +1,47 @@ +# parameters
 +nc: 1  # number of classes
 +depth_multiple: 1.0  # model depth multiple
 +width_multiple: 1.0  # layer channel multiple
 +
 +# anchors
 +anchors:
 +  - [4,5,  8,10,  13,16]  # P3/8
 +  - [23,29,  43,55,  73,105]  # P4/16
 +  - [146,217,  231,300,  335,433]  # P5/32
 +
 +# YOLOv5 backbone
 +backbone:
 +  # [from, number, module, args]
 +  [[-1, 1, StemBlock, [64, 3, 2]],  # 0-P1/2
 +   [-1, 3, C3, [128]],
 +   [-1, 1, Conv, [256, 3, 2]],      # 2-P3/8
 +   [-1, 9, C3, [256]],
 +   [-1, 1, Conv, [512, 3, 2]],      # 4-P4/16
 +   [-1, 9, C3, [512]],
 +   [-1, 1, Conv, [1024, 3, 2]],     # 6-P5/32
 +   [-1, 1, SPP, [1024, [3,5,7]]],
 +   [-1, 3, C3, [1024, False]],      # 8
 +  ]
 +
 +# YOLOv5 head
 +head:
 +  [[-1, 1, Conv, [512, 1, 1]],
 +   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
 +   [[-1, 5], 1, Concat, [1]],  # cat backbone P4
 +   [-1, 3, C3, [512, False]],  # 12
 +
 +   [-1, 1, Conv, [256, 1, 1]],
 +   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
 +   [[-1, 3], 1, Concat, [1]],  # cat backbone P3
 +   [-1, 3, C3, [256, False]],  # 16 (P3/8-small)
 +
 +   [-1, 1, Conv, [256, 3, 2]],
 +   [[-1, 13], 1, Concat, [1]],  # cat head P4
 +   [-1, 3, C3, [512, False]],  # 19 (P4/16-medium)
 +
 +   [-1, 1, Conv, [512, 3, 2]],
 +   [[-1, 9], 1, Concat, [1]],  # cat head P5
 +   [-1, 3, C3, [1024, False]],  # 22 (P5/32-large)
 +
 +   [[16, 19, 22], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
 +  ]
\ No newline at end of file diff --git a/r_facelib/detection/yolov5face/models/yolov5n.yaml b/r_facelib/detection/yolov5face/models/yolov5n.yaml new file mode 100644 index 0000000..0a03fb0 --- /dev/null +++ b/r_facelib/detection/yolov5face/models/yolov5n.yaml @@ -0,0 +1,45 @@ +# parameters
 +nc: 1  # number of classes
 +depth_multiple: 1.0  # model depth multiple
 +width_multiple: 1.0  # layer channel multiple
 +
 +# anchors
 +anchors:
 +  - [4,5,  8,10,  13,16]  # P3/8
 +  - [23,29,  43,55,  73,105]  # P4/16
 +  - [146,217,  231,300,  335,433]  # P5/32
 +
 +# YOLOv5 backbone
 +backbone:
 +  # [from, number, module, args]
 +  [[-1, 1, StemBlock, [32, 3, 2]],    # 0-P2/4
 +   [-1, 1, ShuffleV2Block, [128, 2]], # 1-P3/8
 +   [-1, 3, ShuffleV2Block, [128, 1]], # 2
 +   [-1, 1, ShuffleV2Block, [256, 2]], # 3-P4/16
 +   [-1, 7, ShuffleV2Block, [256, 1]], # 4
 +   [-1, 1, ShuffleV2Block, [512, 2]], # 5-P5/32
 +   [-1, 3, ShuffleV2Block, [512, 1]], # 6
 +  ]
 +
 +# YOLOv5 head
 +head:
 +  [[-1, 1, Conv, [128, 1, 1]],
 +   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
 +   [[-1, 4], 1, Concat, [1]],  # cat backbone P4
 +   [-1, 1, C3, [128, False]],  # 10
 +
 +   [-1, 1, Conv, [128, 1, 1]],
 +   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
 +   [[-1, 2], 1, Concat, [1]],  # cat backbone P3
 +   [-1, 1, C3, [128, False]],  # 14 (P3/8-small)
 +
 +   [-1, 1, Conv, [128, 3, 2]],
 +   [[-1, 11], 1, Concat, [1]],  # cat head P4
 +   [-1, 1, C3, [128, False]],  # 17 (P4/16-medium)
 +
 +   [-1, 1, Conv, [128, 3, 2]],
 +   [[-1, 7], 1, Concat, [1]],  # cat head P5
 +   [-1, 1, C3, [128, False]],  # 20 (P5/32-large)
 +
 +   [[14, 17, 20], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
 +  ]
 | 
