2021SC@SDUSC山东大学软件学院软件工程应用与实践--YOLOV5代码分析(四)general.py-2

2021SC@SDUSC

目录

前言

is_writeable函数

is_docker函数

is_colab函数

is_pip函数

is_ascii函数

file_size函数

check_online函数

emojis函数

check_git_status函数

check_python函数

 check_version函数

check_requirements函数

make_divisible函数

check_img_size函数

check_imshow函数

check_suffix函数

 check_yaml函数

check_file函数

总结


​​​​​​​

前言

这篇文章我们继续对yolov5项目源码进行解读

is_writeable函数

def is_writeable(dir, test=False):
    # Return True if directory has write permissions, test opening a file with write permissions if test=True
    if test:  # method 1
        file = Path(dir) / 'tmp.txt'
        try:
            with open(file, 'w'):  # open file with write permissions
                pass
            file.unlink()  # remove file
            return True
        except IOError:
            return False
    else:  # method 2
        return os.access(dir, os.R_OK)  # possible issues on Windows

参数

dir:测试该目录是否可写,可写则返回true,否则返回false

test:为true时 ,通过创建文件来进行测试

该函数的目标是对给定的目录进行测试,测试当前目录是否允许写。

如果test为true,即通过打开文件进行测试,会在给定的目录下创建tmp.txt,file的值为'dir/tmp.txt',接下来尝试以写的形式打开该文件,如果没有报错,将创建的文件移除并且返回true,如果报错了直接返回false。

test为false时,直接通过os模块看给定目录是否允许写。

is_docker函数

def is_docker():
    # Is environment a Docker container?
    return Path('/workspace').exists()  # or Path('/.dockerenv').exists()

返回当前环境是不是一个docker容器,如果根目录下存在/workspace,说明当前环境是一个docker容器,返回true,不然就返回false

is_colab函数

def is_colab():
    # Is environment a Google Colab instance?
    try:
        import google.colab
        return True
    except Exception as e:
        return False

判断当前环境是不是google colab。

Colaboratory 是一个 Google 研究项目,旨在帮助传播机器学习培训和研究成果。它是一个 Jupyter 笔记本环境,不需要进行任何设置就可以使用,并且完全在云端运行。

Colaboratory 笔记本存储在 Google 云端硬盘中,并且可以共享,就如同使用 Google 文档或表格一样。Colaboratory 可免费使用。

利用Colaboratory ,可以方便的使用Keras,TensorFlow,PyTorch,OpenCV等框架进行深度学习应用的开发。

is_pip函数

def is_pip():
    # Is file in a pip package?
    print(Path(__file__))
    print(Path(__file__).resolve())
    print(Path(__file__).resolve().parts)
    return 'site-packages' in Path(__file__).resolve().parts

判断当前文件是不是在pip包中

Path(__file__).resolve()返回当前文件的路径,.parts将路径拆开,如当前路径为C:\\yolov5\\general.py,parts返回一个列表[C:\\,yolov5,gengral.py]。

is_ascii函数

def is_ascii(s=''):
    # Is string composed of all ASCII (no UTF) characters?
    s = str(s)  # convert list, tuple, None, etc. to str
    return len(s.encode().decode('ascii', 'ignore')) == len(s)

参数

s:需要进行判断的字符串

判断s是不是全部由ASCII码组成,首先将s转换为字符串,然后对s的长度和转换成ascii码后的s的长度进行比较,相等就说明s全部由ascii码组成,就返回true,不相等说明s包含了不是ascii码的字符,就返回false

file_size函数

def file_size(path):
    # Return file/dir size (MB)
    path = Path(path)
    if path.is_file():
        return path.stat().st_size / 1E6
    elif path.is_dir():
        return sum(f.stat().st_size for f in path.glob('**/*') if f.is_file()) / 1E6
    else:
        return 0.0

参数

path:需要计算的路径 

该函数返回给定路径的文件或目录的大小,单位为MB。首先将字符串转换为路径,如果给定路径是个文件,那么直接查询该文件的大小,并将单位转换为MB,如果是个目录,就返回该目录下所有文件的大小之和。

check_online函数

def check_online():
    # Check internet connectivity
    import socket
    try:
        socket.create_connection(("1.1.1.1", 443), 5)  # check host accessibility
        return True
    except OSError:
        return False

判断网络是否可以正常连接。

emojis函数

def emojis(str=''):
    # Return platform-dependent emoji-safe version of string
    return str.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else str

参数

str:需要输出的字符串

根据平台将可能包含表情字符的str安全地输出

check_git_status函数

@try_except
def check_git_status():
    # Recommend 'git pull' if code is out of date
    msg = ', for updates see https://github.com/ultralytics/yolov5'
    print(colorstr('github: '), end='')
    assert Path('.git').exists(), 'skipping check (not a git repository)' + msg
    assert not is_docker(), 'skipping check (Docker image)' + msg
    assert check_online(), 'skipping check (offline)' + msg

    cmd = 'git fetch && git config --get remote.origin.url'
    url = check_output(cmd, shell=True, timeout=5).decode().strip().rstrip('.git')  # git fetch
    branch = check_output('git rev-parse --abbrev-ref HEAD', shell=True).decode().strip()  # checked out
    n = int(check_output(f'git rev-list {branch}..origin/master --count', shell=True))  # commits behind
    if n > 0:
        s = f"⚠️ YOLOv5 is out of date by {n} commit{'s' * (n > 1)}. Use `git pull` or `git clone {url}` to update."
    else:
        s = f'up to date with {url} ✅'
    print(emojis(s))  # emoji-safe

如果当前项目是由github上拉下来的,检查GitHub网站的项目代码是否更新。

msg是输出的提示信息,提供了网站地址

colorstr对字符串上色,使输出更美观

检查是否存在.git文件,即当前项目是否从github下载的

检查当前环境是不是docker容器

检查对网络的连接

接下来到网站获取最新的代码,如果不是最新的代码是进行输出提示,是同样会有输出提示

check_python函数

def check_python(minimum='3.6.2'):
    # Check current python version vs. required python version
    check_version(platform.python_version(), minimum, name='Python ')

 参数

minimun:允许的最小python版本

检查当前python版本是否是允许的最小版本

 check_version函数

def check_version(current='0.0.0', minimum='0.0.0', name='version ', pinned=False):
    # Check version vs. required version
    current, minimum = (pkg.parse_version(x) for x in (current, minimum))
    result = (current == minimum) if pinned else (current >= minimum)
    assert result, f'{name}{minimum} required by YOLOv5, but {name}{current} is currently installed'

参数

current:当前版本号

minimun:允许的最小版本

name:用于方便输出

pinned:为true时当前版本只能与限定的版本相同,为false时,当前版本大于最小版本即可

首先获取给定的每一层版本,如2.1.2,一次获取2,1,2,并进行比较

最后进行输出提示

check_requirements函数

@try_except
def check_requirements(requirements='requirements.txt', exclude=(), install=True):
    # Check installed dependencies meet requirements (pass *.txt file or list of packages)
    prefix = colorstr('red', 'bold', 'requirements:')
    check_python()  # check python version
    if isinstance(requirements, (str, Path)):  # requirements.txt file
        file = Path(requirements)
        assert file.exists(), f"{prefix} {file.resolve()} not found, check failed."
        requirements = [f'{x.name}{x.specifier}' for x in pkg.parse_requirements(file.open()) if x.name not in exclude]
    else:  # list or tuple of packages
        requirements = [x for x in requirements if x not in exclude]

    n = 0  # number of packages updates
    for r in requirements:
        try:
            pkg.require(r)
        except Exception as e:  # DistributionNotFound or VersionConflict if requirements not met
            s = f"{prefix} {r} not found and is required by YOLOv5"
            if install:
                print(f"{s}, attempting auto-update...")
                try:
                    assert check_online(), f"'pip install {r}' skipped (offline)"
                    print(check_output(f"pip install '{r}'", shell=True).decode())
                    n += 1
                except Exception as e:
                    print(f'{prefix} {e}')
            else:
                print(f'{s}. Please install and rerun your command.')

    if n:  # if packages updated
        source = file.resolve() if 'file' in locals() else requirements
        s = f"{prefix} {n} package{'s' * (n > 1)} updated per {source}\n" \
            f"{prefix} ⚠️ {colorstr('bold', 'Restart runtime or rerun command for updates to take effect')}\n"
        print(emojis(s))

参数

requirements:requirements文本文件,也可以是列表或元组,包含了需要满足的包

exclude:可以不用满足的包

install:为true时自动安装不满足的包

函数实现了检查当前环境是否满足给定的requiremens所包含的包

首先检查python版本是否满足

如果requiremens是txt文件或者是路径,获取该路径,判断是否有这个文件,然后获取文件内的内容,给到requirements中

如果是列表或元组,直接转换成列表

若当前遍历的包在exclude中,则不执行

接下来开始进行检查

若不存在这个包,判断install,为true的话就检查联网并自动进行安装,否则输出提示信息让用户自己安装需要的包,当自动安装包时会将n++

 如果自动安装了包,最后会输出相关信息

make_divisible函数

def make_divisible(x, divisor):
    # Returns x evenly divisible by divisor
    return math.ceil(x / divisor) * divisor

参数

x:需要改变的值

divisor:除数 

该函数返回了一个改变之后的x,使x对divisor可整除。先让x除以divisor,取得的数取ceil,再乘以divisor返回。假如x是3,divisor是2,那么就会返回4,为最近的可以整除2的值,如果是floor就是2。 

check_img_size函数

def check_img_size(imgsz, s=32, floor=0):
    # Verify image size is a multiple of stride s in each dimension
    if isinstance(imgsz, int):  # integer i.e. img_size=640
        new_size = max(make_divisible(imgsz, int(s)), floor)
    else:  # list i.e. img_size=[640, 480]
        new_size = [max(make_divisible(x, int(s)), floor) for x in imgsz]
    if new_size != imgsz:
        print(f'WARNING: --img-size {imgsz} must be multiple of max stride {s}, updating to {new_size}')
    return new_size

参数

imgsz:输入的图片的大小

s:因子

floor:最低值

检查imgsz是否可以将s整除。如果不行则进行调整并返回,最小值为floor,imgsz可以是一个数也可以是列表。当imgsz不能整除s时,imgsz的值是修改之后的imgsz的值与floor的最大值,将imgsz限制在了floor之上。

check_imshow函数

def check_imshow():
    # Check if environment supports image displays
    try:
        assert not is_docker(), 'cv2.imshow() is disabled in Docker environments'
        assert not is_colab(), 'cv2.imshow() is disabled in Google Colab environments'
        cv2.imshow('test', np.zeros((1, 1, 3)))
        cv2.waitKey(1)
        cv2.destroyAllWindows()
        cv2.waitKey(1)
        return True
    except Exception as e:
        print(f'WARNING: Environment does not support cv2.imshow() or PIL Image.show() image displays\n{e}')
        return False

检查当前环境是否支持显示图片。由于docker和colab环境不支持显示图片,因此当前环境是这两种环境则直接报错返回false;接下来进行测试,自定义了一个1*1*3大小的图片,即一个像素,每个灰度级都是0,进行显示,然后关闭该窗口,如果该过程都没有问题的话返回true。在整个过程中有任何异常都会被捕捉到返回false。

check_suffix函数

def check_suffix(file='yolov5s.pt', suffix=('.pt',), msg=''):
    # Check file(s) for acceptable suffixes
    if file and suffix:
        if isinstance(suffix, str):
            suffix = [suffix]
        for f in file if isinstance(file, (list, tuple)) else [file]:
            assert Path(f).suffix.lower() in suffix, f"{msg}{f} acceptable suffix is {suffix}"

参数

file:需要检查的文件

suffix:允许的后缀

msg:错误输出信息

该函数对files的后缀进行检查,如果不是要求的后缀会报错。

file和suffix都可以是元组或列表,在函数里会被转换成列表。

首先将suffix转换成列表,然后对file里的每个元素进行检查,如果f的后缀不在给定的后缀当中,输出错误信息,抛出异常。

 check_yaml函数

def check_yaml(file, suffix=('.yaml', '.yml')):
    # Search/download YAML file (if necessary) and return path, checking suffix
    return check_file(file, suffix)

参数

file:需要检查的文件

suffix:要求的后缀名

检查给定文件是否是yaml文件

check_file函数

 

def check_file(file, suffix=''):
    # Search/download file (if necessary) and return path
    check_suffix(file, suffix)  # optional
    file = str(file)  # convert to str()
    if Path(file).is_file() or file == '':  # exists
        return file
    elif file.startswith(('http:/', 'https:/')):  # download
        url = str(Path(file)).replace(':/', '://')  # Pathlib turns :// -> :/
        file = Path(urllib.parse.unquote(file)).name.split('?')[0]  # '%2F' to '/', split https://url.com/file.txt?auth
        print(f'Downloading {url} to {file}...')
        torch.hub.download_url_to_file(url, file)
        assert Path(file).exists() and Path(file).stat().st_size > 0, f'File download failed: {url}'  # check
        return file
    else:  # search
        files = glob.glob('./**/' + file, recursive=True)  # find file
        assert len(files), f'File not found: {file}'  # assert file was found
        assert len(files) == 1, f"Multiple files match '{file}', specify exact path: {files}"  # assert unique
        return files[0]  # return file

参数

file:文件名或网址

suffix:后缀名

 该函数实现了在目录中搜索文件或是在网络上下载文件

首先对file的后缀进行检查,然后将file转换成str

如果file是文件名或为空,直接返回file

如果file是个网址,会下载该网址的内容,新建一个file,这个file存放下载的内容

如果都不满足,即当前目录下不存在file这个文件,则会在目录下所有子目录搜索该文件,当有多个重名的文件时返回第一个找到的文件

总结

这部分代码基本都是一些检查是否符合规范的,算不上项目的核心部分,由于算法的核心部分主要是在定义模型上,为了体现我对算法的理解,在后面我会用理论的方式来讲解核心算法。

你可能感兴趣的:(yolov5,python)