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项目源码进行解读
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模块看给定目录是否允许写。
def is_docker():
# Is environment a Docker container?
return Path('/workspace').exists() # or Path('/.dockerenv').exists()
返回当前环境是不是一个docker容器,如果根目录下存在/workspace,说明当前环境是一个docker容器,返回true,不然就返回false
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等框架进行深度学习应用的开发。
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]。
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
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,如果是个目录,就返回该目录下所有文件的大小之和。
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
判断网络是否可以正常连接。
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安全地输出
@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容器
检查对网络的连接
接下来到网站获取最新的代码,如果不是最新的代码是进行输出提示,是同样会有输出提示
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版本是否是允许的最小版本
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,并进行比较
最后进行输出提示
@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++
如果自动安装了包,最后会输出相关信息
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。
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之上。
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。
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的后缀不在给定的后缀当中,输出错误信息,抛出异常。
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文件
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这个文件,则会在目录下所有子目录搜索该文件,当有多个重名的文件时返回第一个找到的文件
这部分代码基本都是一些检查是否符合规范的,算不上项目的核心部分,由于算法的核心部分主要是在定义模型上,为了体现我对算法的理解,在后面我会用理论的方式来讲解核心算法。