Commit 0d97cc8c authored by Sugon_ldc's avatar Sugon_ldc
Browse files

add new model

parents
Pipeline #316 failed with stages
in 0 seconds
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import os
import os.path as osp
import logging
from datetime import datetime
from qtpy.QtWidgets import QApplication # 导入PyQt相关模块
from qtpy import QtCore
from eiseg import pjpath
from app import APP_EISeg # 导入带槽的界面
def main():
## -- log --
settings = QtCore.QSettings(
osp.join(pjpath, "config/setting.txt"), QtCore.QSettings.IniFormat)
#
# logFolder = settings.value("logFolder")
# logLevel = settings.value("logLevel")
# logDays = settings.value("logDays")
#
# if logFolder is None or len(logFolder) == 0:
# logFolder = osp.normcase(osp.join(pjpath, "log"))
# if not osp.exists(logFolder):
# os.makedirs(logFolder)
#
# if logLevel:
# logLevel = eval(logLevel)
# else:
# logLevel = logging.DEBUG
# if logDays:
# logDays = int(logDays)
# else:
# logDays = 7
# # TODO: 删除大于logDays 的 log
#
# t = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
# logging.basicConfig(
# level=logging.DEBUG,
# filename=osp.normcase(osp.join(logFolder, f"eiseg-{t}.log")),
# format="%(levelname)s - %(asctime)s - %(filename)s - %(funcName)s - %(message)s",
# )
# logger = logging.getLogger("EISeg Logger")
# handler = logging.FileHandler(osp.normcase(osp.join(logFolder, f"eiseg-{t}.log")))
#
# handler.setFormatter(
# logging.Formatter(
# "%(levelname)s - %(asctime)s - %(filename)s - %(funcName)s - %(message)s"
# )
# )
# logger.addHandler(handler)
# logger.info("test info")
#
app = QApplication(sys.argv)
lang = settings.value("language")
if lang != "中文":
trans = QtCore.QTranslator(app)
trans.load(osp.join(pjpath, f"util/translate/{lang}"))
app.installTranslator(trans)
window = APP_EISeg() # 创建对象
window.currLanguage = lang
window.showMaximized() # 全屏显示窗口
# 加载近期模型
QApplication.processEvents()
window.loadRecentModelParam()
window.loadVideoRecentModelParam()
sys.exit(app.exec())
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os.path as osp
from functools import partial
from qtpy import QtCore, QtGui, QtWidgets
from qtpy.QtGui import QIcon
from qtpy.QtCore import QTimer
from eiseg import pjpath, __APPNAME__, __VERSION__, logger
from eiseg.widget.create import creat_dock, create_button, create_slider, create_text
from widget import AnnotationScene, AnnotationView
from widget.create import *
from widget.table import TableWidget
from widget.vtk import VTKWidget
# log = logging.getLogger(__name__ + ".ui")
class Ui_EISeg(object):
def __init__(self):
super(Ui_EISeg, self).__init__()
self.tr = partial(QtCore.QCoreApplication.translate, "APP_EISeg")
def setupUi(self, MainWindow):
## -- 主窗体设置 --
MainWindow.setObjectName("MainWindow")
MainWindow.setMinimumSize(QtCore.QSize(1366, 768)) # 1366x768的屏幕显示不全
MainWindow.setWindowTitle(__APPNAME__ + " " + __VERSION__)
MainWindow.setWindowIcon(QIcon()) # TODO: 默认图标需要换一个吗,貌似不能不显示图标
self.CentralWidget = QtWidgets.QWidget(MainWindow)
self.CentralWidget.setObjectName("CentralWidget")
MainWindow.setCentralWidget(self.CentralWidget)
## -----
## -- 工具栏 --
toolBar = QtWidgets.QToolBar(self)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred,
QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(toolBar.sizePolicy().hasHeightForWidth())
toolBar.setSizePolicy(sizePolicy)
toolBar.setMinimumSize(QtCore.QSize(0, 33))
toolBar.setMovable(True)
toolBar.setAllowedAreas(QtCore.Qt.BottomToolBarArea |
QtCore.Qt.TopToolBarArea)
toolBar.setObjectName("toolBar")
self.toolBar = toolBar
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
## -----
## -- 状态栏 --
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
self.statusbar.setStyleSheet("QStatusBar::item {border: none;}")
MainWindow.setStatusBar(self.statusbar)
self.statusbar.addPermanentWidget(
self.show_logo(osp.join(pjpath, "resource/Paddle.png")))
## -----
## -- 图形区域 --
ImageRegion = QtWidgets.QHBoxLayout(self.CentralWidget)
ImageRegion.setObjectName("ImageRegion")
# 滑动区域
self.scrollArea = QtWidgets.QScrollArea(self.CentralWidget)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
ImageRegion.addWidget(self.scrollArea)
# 图形显示
self.scene = AnnotationScene()
self.scene.addPixmap(QtGui.QPixmap())
self.canvas = AnnotationView(self.scene, self)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
self.canvas.setSizePolicy(sizePolicy)
self.canvas.setAlignment(QtCore.Qt.AlignCenter)
self.canvas.setAutoFillBackground(False)
self.canvas.setStyleSheet("background-color: White")
self.canvas.setObjectName("canvas")
self.scrollArea.setWidget(self.canvas)
## -----
## -- 工作区 --
self.p_create_dock = partial(self.creat_dock, MainWindow)
self.p_create_button = partial(self.create_button, self.CentralWidget)
# 模型加载
widget = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(widget)
self.ModelDock = self.p_create_dock("ModelDock",
self.tr("模型选择"), widget)
ModelRegion = QtWidgets.QVBoxLayout()
ModelRegion.setObjectName("ModelRegion")
# labShowSet = self.create_text(CentralWidget, "labShowSet", "模型选择")
# ModelRegion.addWidget(labShowSet)
# combo = QtWidgets.QComboBox(self)
# # combo.addItems([self.tr(ModelsNick[m.__name__][0]) for m in MODELS])
# combo.addItems([self.tr(ModelsNick[m][0]) for m in ModelsNick.keys()])
# self.comboModelSelect = combo
# ModelRegion.addWidget(self.comboModelSelect)
# 网络参数
self.btnParamsSelect = self.p_create_button(
"btnParamsLoad",
self.tr("加载网络参数"),
osp.join(pjpath, "resource/Model.png"),
"Ctrl+D", )
ModelRegion.addWidget(self.btnParamsSelect) # 模型选择
self.cheWithMask = QtWidgets.QCheckBox(self)
self.cheWithMask.setText(self.tr("使用掩膜"))
self.cheWithMask.setChecked(True)
ModelRegion.addWidget(self.cheWithMask) # with_mask
ModelRegion.addSpacerItem(
QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum,
QtWidgets.QSizePolicy.Expanding))
horizontalLayout.addLayout(ModelRegion)
MainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.ModelDock)
# 数据列表
# TODO: 数据列表加一个搜索功能
widget = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(widget)
ListRegion = QtWidgets.QVBoxLayout()
ListRegion.setObjectName("ListRegion")
# labFiles = self.create_text(CentralWidget, "labFiles", "数据列表")
# ListRegion.addWidget(labFiles)
self.listFiles = QtWidgets.QListWidget(self.CentralWidget)
self.listFiles.setObjectName("ListFiles")
ListRegion.addWidget(self.listFiles)
# ListRegion.addWidget(self.btnSave)
horizontalLayout.addLayout(ListRegion)
self.DataDock = self.p_create_dock("DataDock", self.tr("数据列表"), widget)
MainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.DataDock)
# 标签列表
widget = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(widget)
LabelRegion = QtWidgets.QVBoxLayout()
LabelRegion.setObjectName("LabelRegion")
self.labelListTable = TableWidget(
self.CentralWidget) # QtWidgets.QTableWidget(CentralWidget)
self.labelListTable.horizontalHeader().hide()
# 铺满
self.labelListTable.horizontalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch)
self.labelListTable.verticalHeader().hide()
self.labelListTable.setColumnWidth(0, 10)
# self.labelListTable.setMinimumWidth()
self.labelListTable.setObjectName("labelListTable")
self.labelListTable.clearContents()
self.labelListTable.setRowCount(0)
self.labelListTable.setColumnCount(4)
LabelRegion.addWidget(self.labelListTable)
self.btnAddClass = self.p_create_button(
"btnAddClass",
self.tr("添加标签"), osp.join(pjpath, "resource/Label.png"))
LabelRegion.addWidget(self.btnAddClass)
horizontalLayout.addLayout(LabelRegion)
self.LabelDock = self.p_create_dock("LabelDock",
self.tr("标签列表"), widget)
MainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.LabelDock)
## 滑块设置
# 分割阈值
self.p_create_slider = partial(self.create_slider, self.CentralWidget)
widget = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(widget)
ShowSetRegion = QtWidgets.QVBoxLayout()
ShowSetRegion.setObjectName("ShowSetRegion")
self.sldThresh, _, SegShowRegion = self.p_create_slider(
"sldThresh", "labThresh", self.tr("分割阈值:"))
ShowSetRegion.addLayout(SegShowRegion)
ShowSetRegion.addWidget(self.sldThresh)
# 透明度
self.sldOpacity, _, MaskShowRegion = self.p_create_slider(
"sldOpacity", "labOpacity", self.tr("标签透明度:"), 75)
ShowSetRegion.addLayout(MaskShowRegion)
ShowSetRegion.addWidget(self.sldOpacity)
# 点大小
self.sldClickRadius, _, PointShowRegion = self.p_create_slider(
"sldClickRadius", "labClickRadius",
self.tr("点击可视化半径:"), 3, 10, 0, 1)
ShowSetRegion.addLayout(PointShowRegion)
ShowSetRegion.addWidget(self.sldClickRadius)
# 保存
self.btnSave = self.p_create_button(
"btnSave",
self.tr("保存"),
osp.join(pjpath, "resource/Save.png"),
"Ctrl+S", )
ShowSetRegion.addWidget(self.btnSave)
ShowSetRegion.addSpacerItem(
QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum,
QtWidgets.QSizePolicy.Expanding))
horizontalLayout.addLayout(ShowSetRegion)
self.SegSettingDock = self.p_create_dock("SegSettingDock",
self.tr("分割设置"), widget)
MainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea,
self.SegSettingDock)
## 专业功能区工作区
## 遥感影像设置
widget = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(widget)
self.RSDock = self.p_create_dock("RSDock", self.tr("遥感设置"), widget)
bandRegion = QtWidgets.QVBoxLayout()
bandRegion.setObjectName("bandRegion")
bandRegion.addWidget(
create_text(self.CentralWidget, "bandSelection", self.tr("波段设置")))
text_list = ["R", "G", "B"]
self.bandCombos = []
for txt in text_list:
combo = QtWidgets.QComboBox()
combo.addItems(["band_1"])
self.bandCombos.append(combo)
hbandLayout = QtWidgets.QHBoxLayout()
hbandLayout.setObjectName("hbandLayout")
hbandLayout.addWidget(
create_text(self.CentralWidget, "band" + txt, txt))
hbandLayout.addWidget(combo)
hbandLayout.setStretch(1, 4)
bandRegion.addLayout(hbandLayout)
bandRegion.addWidget(
create_text(self.CentralWidget, "resultSave", self.tr("保存设置")))
self.boundaryRegular = QtWidgets.QCheckBox(self.tr("建筑边界规范化"))
self.boundaryRegular.setObjectName("boundaryRegular")
bandRegion.addWidget(self.boundaryRegular)
self.shpSave = QtWidgets.QCheckBox(self.tr("另存为shapefile"))
self.shpSave.setObjectName("shpSave")
bandRegion.addWidget(self.shpSave)
horizontalLayout.addLayout(bandRegion)
bandRegion.addWidget(
create_text(self.CentralWidget, "showGeoInfo", self.tr("地理信息")))
self.edtGeoinfo = QtWidgets.QTextEdit(self.tr("无"))
self.edtGeoinfo.setObjectName("edtGeoinfo")
self.edtGeoinfo.setReadOnly(True)
bandRegion.addWidget(self.edtGeoinfo)
MainWindow.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.RSDock)
## 医学影像设置
widget = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(widget)
MIRegion = QtWidgets.QVBoxLayout()
MIRegion.setObjectName("MIRegion")
# mi_text = create_text(CentralWidget, "sliceSelection", self.tr("切片选择"))
# MIRegion.addWidget(mi_text)
# self.sldMISlide, _, slideRegion = p_create_slider(
# "sldMISlide", "labMISlide", self.tr("切片选择:"), 1, 1, 1
# )
# self.sldMISlide.setMinimum(1)
# MIRegion.addLayout(slideRegion)
# MIRegion.addWidget(self.sldMISlide)
self.sldWw, self.textWw, WwRegion = self.p_create_slider(
"sldWw", "textWw", self.tr("窗宽:"), 200, 2048, -2048, 1, True)
MIRegion.addLayout(WwRegion)
MIRegion.addWidget(self.sldWw)
self.sldWc, self.textWc, WcRegion = self.p_create_slider(
"sldWc", "textWc", self.tr("窗位:"), 0, 2048, -2048, 1, True)
MIRegion.addLayout(WcRegion)
MIRegion.addWidget(self.sldWc)
MIRegion.addSpacerItem(
QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum,
QtWidgets.QSizePolicy.Expanding))
horizontalLayout.addLayout(MIRegion)
self.MedDock = self.p_create_dock("MedDock", self.tr("医疗设置"), widget)
MainWindow.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.MedDock)
## 宫格区域设置
widget = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(widget)
self.GridDock = self.p_create_dock("GridDock", self.tr("宫格切换"), widget)
GridRegion = QtWidgets.QVBoxLayout()
GridRegion.setObjectName("GridRegion")
# self.btnInitGrid = p_create_button(
# "btnInitGrid",
# self.tr("创建宫格"),
# osp.join(pjpath, "resource/N2.png"),
# "",
# )
self.btnFinishedGrid = self.p_create_button(
"btnFinishedGrid",
self.tr("完成宫格"),
osp.join(pjpath, "resource/Save.png"),
"", )
hbandLayout = QtWidgets.QHBoxLayout()
hbandLayout.setObjectName("hbandLayout")
# hbandLayout.addWidget(self.btnInitGrid)
hbandLayout.addWidget(self.btnFinishedGrid)
GridRegion.addLayout(hbandLayout) # 创建宫格
self.cheSaveEvery = QtWidgets.QCheckBox(self)
self.cheSaveEvery.setText(self.tr("保存每个宫格的标签"))
self.cheSaveEvery.setChecked(False)
GridRegion.addWidget(self.cheSaveEvery)
self.gridTable = QtWidgets.QTableWidget(self.CentralWidget)
self.gridTable.horizontalHeader().hide()
self.gridTable.verticalHeader().hide()
# 铺满
self.gridTable.horizontalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch)
self.gridTable.verticalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch)
self.gridTable.setObjectName("gridTable")
self.gridTable.clearContents()
self.gridTable.setColumnCount(1)
self.gridTable.setRowCount(1)
GridRegion.addWidget(self.gridTable)
horizontalLayout.addLayout(GridRegion)
MainWindow.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.GridDock)
## 视频设置
widget = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(widget)
self.VSTDock = self.p_create_dock("VSTDock", self.tr("视频分割设置"), widget)
VSTDock = QtWidgets.QVBoxLayout()
VSTDock.setObjectName("VSTDock")
self.btn3DParamsSelect = self.p_create_button(
"btn3DParamsLoad",
self.tr("加载传播网络参数"),
osp.join(pjpath, "resource/Model.png"),
"", )
VSTDock.addWidget(self.btn3DParamsSelect)
self.btnPropagate = self.p_create_button(
"btnPropagate",
self.tr("传播"),
osp.join(pjpath, "resource/Propagate.png"),
"", )
self.proPropagete = QtWidgets.QProgressBar(self.CentralWidget)
self.proPropagete.setAlignment(QtCore.Qt.AlignVCenter)
self.proPropagete.setValue(0)
VSTDock.addWidget(self.btnPropagate)
proLayer = QtWidgets.QHBoxLayout()
proLayer.addWidget(
create_text(self.CentralWidget, None, self.tr("进度:")))
proLayer.addWidget(self.proPropagete)
VSTDock.addLayout(proLayer)
VSTDock.addSpacerItem(
QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum,
QtWidgets.QSizePolicy.Expanding))
horizontalLayout.addLayout(VSTDock)
MainWindow.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.VSTDock)
# 3d显示
widget = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(widget)
self.TDDock = self.p_create_dock("TDDock", self.tr("3D显示"), widget)
TDRegion = QtWidgets.QVBoxLayout()
TDRegion.setObjectName("TDRegion")
self.vtkWidget = VTKWidget(self.CentralWidget)
TDRegion.addWidget(self.vtkWidget)
horizontalLayout.addLayout(TDRegion)
MainWindow.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.TDDock)
# 视频播放功能区域
widget = QtWidgets.QWidget()
verticalLayout = QtWidgets.QVBoxLayout(widget)
self.VideoDock = self.p_create_dock("VideoDock", self.tr("时间轴"), widget)
VideoRegion = QtWidgets.QHBoxLayout()
VideoRegion.setObjectName("VideoRegion")
self.videoPlay = self.p_create_button(
"videoPlay",
self.tr("播放"),
osp.join(pjpath, "resource/Play.png"),
"", )
height = self.videoPlay.height()
min_size = QtCore.QSize(0, height)
VideoRegion.addWidget(self.videoPlay)
self.sldTime, self.textTime, _ = self.p_create_slider(
"sldTime", "textTime", "", 0, 100, 0, 1, True)
self.textTime.setMinimumSize(min_size)
self.sldTime.setTickInterval(1)
self.sldTime.setTickPosition(QtWidgets.QSlider.TicksBelow)
VideoRegion.addWidget(self.sldTime)
VideoRegion.addWidget(
create_text(self.CentralWidget, None, self.tr("帧数:")))
VideoRegion.addWidget(self.textTime)
self.preFrameButton = self.p_create_button(
"preFrameButton",
self.tr("上一帧"), )
self.nextFrameButton = self.p_create_button(
"nextFrameButton",
self.tr("下一帧"), )
# self.preFrameButton = QtWidgets.QPushButton("上一帧")
# self.nextFrameButton = QtWidgets.QPushButton("下一帧")
VideoRegion.addWidget(self.preFrameButton)
VideoRegion.addWidget(self.nextFrameButton)
self.speedComboBox = QtWidgets.QComboBox()
self.speedComboBox.setMinimumSize(min_size)
v_txt = self.tr("倍速: ")
self.speedComboBox.addItems([
v_txt + "0.25x",
v_txt + "0.5x",
v_txt + "1.0x",
v_txt + "2.0x",
v_txt + "3.0x",
v_txt + "5.0x",
v_txt + "10.0x",
])
self.speedComboBox.setCurrentIndex(2)
VideoRegion.addWidget(self.speedComboBox)
verticalLayout.addLayout(VideoRegion)
self.VideoDock.setAllowedAreas(QtCore.Qt.BottomDockWidgetArea)
MainWindow.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.VideoDock)
# timer
self.timer = QTimer()
self.timer.setSingleShot(False)
## -----
QtCore.QMetaObject.connectSlotsByName(MainWindow)
# log.debug("Set up UI finished")
## 创建文本
def create_text(self, parent, text_name=None, text_text=None):
return create_text(parent, text_name, text_text)
## 创建按钮
def create_button(self,
parent,
btn_name,
btn_text,
ico_path=None,
curt=None):
return create_button(parent, btn_name, btn_text, ico_path, curt)
## 创建dock
def creat_dock(self, parent, name, text, layout):
return creat_dock(parent, name, text, layout)
## 显示Logo
def show_logo(self, logo_path):
labLogo = QtWidgets.QLabel()
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum,
QtWidgets.QSizePolicy.Maximum)
labLogo.setSizePolicy(sizePolicy)
labLogo.setMaximumSize(QtCore.QSize(100, 33))
labLogo.setPixmap(QtGui.QPixmap(logo_path))
labLogo.setScaledContents(True)
labLogo.setObjectName("labLogo")
return labLogo
## 创建滑块区域
def create_slider(self,
parent,
sld_name,
text_name,
text,
default_value=50,
max_value=100,
min_value=0,
text_rate=0.01,
edit=False):
return create_slider(parent, sld_name, text_name, text, default_value,
max_value, min_value, text_rate, edit)
from .qt import newAction, addActions, struct, newIcon
from .config import parse_configs, save_configs
from .colormap import colorMap
from .polygon import get_polygon, Instructions
from .manager import MODELS
from .language import TransUI
from .coco.coco import COCO
from .label import LabelList
from .opath import check_cn, normcase
from .palette import pal_color_map, color_map
\ No newline at end of file
cocoDict = {
"info": info,
"images": [image],
"annotations": [annotation],
"categories": [
{
"id": int,
"name": str,
"supercategory": str,
}
],
"licenses": [license],
}
license = {
"id": int,
"name": str,
"url": str,
}
image = {
"id": int,
"width": int,
"height": int,
"file_name": str,
"license": int,
"flickr_url": str,
"coco_url": str,
"date_captured": datetime,
}
annotation = {
"id": int,
"image_id": int,
"category_id": int,
"segmentation": [polygon],
"area": float,
"bbox": [x, y, width, height],
}
info = {
"year": int,
"version": str,
"description": str,
"contributor": str,
"url": str,
"date_created": datetime,
}
import datetime
class CoCoAnn:
def __init__(self, cocoFile=None):
self.dict = {
"info": {},
"images": [],
"annotations": [],
"categories": [],
"licenses": [],
}
self.annId = 0
def setInfo(
self,
year: int = "",
version="",
description="",
contributor="",
url="",
date_created="",
):
# if not year:
# now = datetime.now()
# year = now.strftime("%Y")
# # TODO: datetime
# if not date_created:
# pass
self.dict["info"] = {
"year": year,
"version": version,
"description": description,
"contributor": contributor,
"url": url,
"date_created": date_created,
}
def setCategories(self, categories):
self.dict["categories"] = categories
def addCategory(self, id, name, supercategory=""):
cat = {
"id": int,
"name": str,
"supercategory": str,
}
self.dict["categories"].append(cat)
def setLicenses(self, licenses):
self.licenses = licenses
def addLicense(self, id, name, url):
license = {
"id": int,
"name": str,
"url": str,
}
self.dict["licenses"].append(license)
def addImage(
self,
id,
width,
height,
file_name,
license="",
flickr_url="",
coco_url="",
date_captured="",
):
image = {
"id": id,
"width": width,
"height": height,
"file_name": file_name,
"license": license,
"flickr_url": flickr_url,
"coco_url": coco_url,
"date_captured": date_captured,
}
self.dict["images"].append(image)
def addAnnotation(
self,
image_id,
category_id,
segmentation,
bbox,
area,
id,
):
{
"id": int,
"image_id": int,
"category_id": int,
"segmentation": [polygon],
"area": float,
"bbox": [x, y, width, height],
}
# distutils: language = c
# distutils: sources = ../common/maskApi.c
#**************************************************************************
# Microsoft COCO Toolbox. version 2.0
# Data, paper, and tutorials available at: http://mscoco.org/
# Code written by Piotr Dollar and Tsung-Yi Lin, 2015.
# Licensed under the Simplified BSD License [see coco/license.txt]
#**************************************************************************
__author__ = 'tsungyi'
import sys
PYTHON_VERSION = sys.version_info[0]
# import both Python-level and C-level symbols of Numpy
# the API uses Numpy to interface C and Python
import numpy as np
cimport numpy as np
from libc.stdlib cimport malloc, free
# intialized Numpy. must do.
np.import_array()
# import numpy C function
# we use PyArray_ENABLEFLAGS to make Numpy ndarray responsible to memoery management
cdef extern from "numpy/arrayobject.h":
void PyArray_ENABLEFLAGS(np.ndarray arr, int flags)
# Declare the prototype of the C functions in MaskApi.h
cdef extern from "maskApi.h":
ctypedef unsigned int uint
ctypedef unsigned long siz
ctypedef unsigned char byte
ctypedef double* BB
ctypedef struct RLE:
siz h,
siz w,
siz m,
uint* cnts,
void rlesInit( RLE **R, siz n )
void rleEncode( RLE *R, const byte *M, siz h, siz w, siz n )
void rleDecode( const RLE *R, byte *mask, siz n )
void rleMerge( const RLE *R, RLE *M, siz n, int intersect )
void rleArea( const RLE *R, siz n, uint *a )
void rleIou( RLE *dt, RLE *gt, siz m, siz n, byte *iscrowd, double *o )
void bbIou( BB dt, BB gt, siz m, siz n, byte *iscrowd, double *o )
void rleToBbox( const RLE *R, BB bb, siz n )
void rleFrBbox( RLE *R, const BB bb, siz h, siz w, siz n )
void rleFrPoly( RLE *R, const double *xy, siz k, siz h, siz w )
char* rleToString( const RLE *R )
void rleFrString( RLE *R, char *s, siz h, siz w )
# python class to wrap RLE array in C
# the class handles the memory allocation and deallocation
cdef class RLEs:
cdef RLE *_R
cdef siz _n
def __cinit__(self, siz n =0):
rlesInit(&self._R, n)
self._n = n
# free the RLE array here
def __dealloc__(self):
if self._R is not NULL:
for i in range(self._n):
free(self._R[i].cnts)
free(self._R)
def __getattr__(self, key):
if key == 'n':
return self._n
raise AttributeError(key)
# python class to wrap Mask array in C
# the class handles the memory allocation and deallocation
cdef class Masks:
cdef byte *_mask
cdef siz _h
cdef siz _w
cdef siz _n
def __cinit__(self, h, w, n):
self._mask = <byte*> malloc(h*w*n* sizeof(byte))
self._h = h
self._w = w
self._n = n
# def __dealloc__(self):
# the memory management of _mask has been passed to np.ndarray
# it doesn't need to be freed here
# called when passing into np.array() and return an np.ndarray in column-major order
def __array__(self):
cdef np.npy_intp shape[1]
shape[0] = <np.npy_intp> self._h*self._w*self._n
# Create a 1D array, and reshape it to fortran/Matlab column-major array
ndarray = np.PyArray_SimpleNewFromData(1, shape, np.NPY_UINT8, self._mask).reshape((self._h, self._w, self._n), order='F')
# The _mask allocated by Masks is now handled by ndarray
PyArray_ENABLEFLAGS(ndarray, np.NPY_OWNDATA)
return ndarray
# internal conversion from Python RLEs object to compressed RLE format
def _toString(RLEs Rs):
cdef siz n = Rs.n
cdef bytes py_string
cdef char* c_string
objs = []
for i in range(n):
c_string = rleToString( <RLE*> &Rs._R[i] )
py_string = c_string
objs.append({
'size': [Rs._R[i].h, Rs._R[i].w],
'counts': py_string
})
free(c_string)
return objs
# internal conversion from compressed RLE format to Python RLEs object
def _frString(rleObjs):
cdef siz n = len(rleObjs)
Rs = RLEs(n)
cdef bytes py_string
cdef char* c_string
for i, obj in enumerate(rleObjs):
if PYTHON_VERSION == 2:
py_string = str(obj['counts']).encode('utf8')
elif PYTHON_VERSION == 3:
py_string = str.encode(obj['counts']) if type(obj['counts']) == str else obj['counts']
else:
raise Exception('Python version must be 2 or 3')
c_string = py_string
rleFrString( <RLE*> &Rs._R[i], <char*> c_string, obj['size'][0], obj['size'][1] )
return Rs
# encode mask to RLEs objects
# list of RLE string can be generated by RLEs member function
def encode(np.ndarray[np.uint8_t, ndim=3, mode='fortran'] mask):
h, w, n = mask.shape[0], mask.shape[1], mask.shape[2]
cdef RLEs Rs = RLEs(n)
rleEncode(Rs._R,<byte*>mask.data,h,w,n)
objs = _toString(Rs)
return objs
# decode mask from compressed list of RLE string or RLEs object
def decode(rleObjs):
cdef RLEs Rs = _frString(rleObjs)
h, w, n = Rs._R[0].h, Rs._R[0].w, Rs._n
masks = Masks(h, w, n)
rleDecode(<RLE*>Rs._R, masks._mask, n);
return np.array(masks)
def merge(rleObjs, intersect=0):
cdef RLEs Rs = _frString(rleObjs)
cdef RLEs R = RLEs(1)
rleMerge(<RLE*>Rs._R, <RLE*> R._R, <siz> Rs._n, intersect)
obj = _toString(R)[0]
return obj
def area(rleObjs):
cdef RLEs Rs = _frString(rleObjs)
cdef uint* _a = <uint*> malloc(Rs._n* sizeof(uint))
rleArea(Rs._R, Rs._n, _a)
cdef np.npy_intp shape[1]
shape[0] = <np.npy_intp> Rs._n
a = np.array((Rs._n, ), dtype=np.uint8)
a = np.PyArray_SimpleNewFromData(1, shape, np.NPY_UINT32, _a)
PyArray_ENABLEFLAGS(a, np.NPY_OWNDATA)
return a
# iou computation. support function overload (RLEs-RLEs and bbox-bbox).
def iou( dt, gt, pyiscrowd ):
def _preproc(objs):
if len(objs) == 0:
return objs
if type(objs) == np.ndarray:
if len(objs.shape) == 1:
objs = objs.reshape((objs[0], 1))
# check if it's Nx4 bbox
if not len(objs.shape) == 2 or not objs.shape[1] == 4:
raise Exception('numpy ndarray input is only for *bounding boxes* and should have Nx4 dimension')
objs = objs.astype(np.double)
elif type(objs) == list:
# check if list is in box format and convert it to np.ndarray
isbox = np.all(np.array([(len(obj)==4) and ((type(obj)==list) or (type(obj)==np.ndarray)) for obj in objs]))
isrle = np.all(np.array([type(obj) == dict for obj in objs]))
if isbox:
objs = np.array(objs, dtype=np.double)
if len(objs.shape) == 1:
objs = objs.reshape((1,objs.shape[0]))
elif isrle:
objs = _frString(objs)
else:
raise Exception('list input can be bounding box (Nx4) or RLEs ([RLE])')
else:
raise Exception('unrecognized type. The following type: RLEs (rle), np.ndarray (box), and list (box) are supported.')
return objs
def _rleIou(RLEs dt, RLEs gt, np.ndarray[np.uint8_t, ndim=1] iscrowd, siz m, siz n, np.ndarray[np.double_t, ndim=1] _iou):
rleIou( <RLE*> dt._R, <RLE*> gt._R, m, n, <byte*> iscrowd.data, <double*> _iou.data )
def _bbIou(np.ndarray[np.double_t, ndim=2] dt, np.ndarray[np.double_t, ndim=2] gt, np.ndarray[np.uint8_t, ndim=1] iscrowd, siz m, siz n, np.ndarray[np.double_t, ndim=1] _iou):
bbIou( <BB> dt.data, <BB> gt.data, m, n, <byte*> iscrowd.data, <double*>_iou.data )
def _len(obj):
cdef siz N = 0
if type(obj) == RLEs:
N = obj.n
elif len(obj)==0:
pass
elif type(obj) == np.ndarray:
N = obj.shape[0]
return N
# convert iscrowd to numpy array
cdef np.ndarray[np.uint8_t, ndim=1] iscrowd = np.array(pyiscrowd, dtype=np.uint8)
# simple type checking
cdef siz m, n
dt = _preproc(dt)
gt = _preproc(gt)
m = _len(dt)
n = _len(gt)
if m == 0 or n == 0:
return []
if not type(dt) == type(gt):
raise Exception('The dt and gt should have the same data type, either RLEs, list or np.ndarray')
# define local variables
cdef double* _iou = <double*> 0
cdef np.npy_intp shape[1]
# check type and assign iou function
if type(dt) == RLEs:
_iouFun = _rleIou
elif type(dt) == np.ndarray:
_iouFun = _bbIou
else:
raise Exception('input data type not allowed.')
_iou = <double*> malloc(m*n* sizeof(double))
iou = np.zeros((m*n, ), dtype=np.double)
shape[0] = <np.npy_intp> m*n
iou = np.PyArray_SimpleNewFromData(1, shape, np.NPY_DOUBLE, _iou)
PyArray_ENABLEFLAGS(iou, np.NPY_OWNDATA)
_iouFun(dt, gt, iscrowd, m, n, iou)
return iou.reshape((m,n), order='F')
def toBbox( rleObjs ):
cdef RLEs Rs = _frString(rleObjs)
cdef siz n = Rs.n
cdef BB _bb = <BB> malloc(4*n* sizeof(double))
rleToBbox( <const RLE*> Rs._R, _bb, n )
cdef np.npy_intp shape[1]
shape[0] = <np.npy_intp> 4*n
bb = np.array((1,4*n), dtype=np.double)
bb = np.PyArray_SimpleNewFromData(1, shape, np.NPY_DOUBLE, _bb).reshape((n, 4))
PyArray_ENABLEFLAGS(bb, np.NPY_OWNDATA)
return bb
def frBbox(np.ndarray[np.double_t, ndim=2] bb, siz h, siz w ):
cdef siz n = bb.shape[0]
Rs = RLEs(n)
rleFrBbox( <RLE*> Rs._R, <const BB> bb.data, h, w, n )
objs = _toString(Rs)
return objs
def frPoly( poly, siz h, siz w ):
cdef np.ndarray[np.double_t, ndim=1] np_poly
n = len(poly)
Rs = RLEs(n)
for i, p in enumerate(poly):
np_poly = np.array(p, dtype=np.double, order='F')
rleFrPoly( <RLE*>&Rs._R[i], <const double*> np_poly.data, int(len(p)/2), h, w )
objs = _toString(Rs)
return objs
def frUncompressedRLE(ucRles, siz h, siz w):
cdef np.ndarray[np.uint32_t, ndim=1] cnts
cdef RLE R
cdef uint *data
n = len(ucRles)
objs = []
for i in range(n):
Rs = RLEs(1)
cnts = np.array(ucRles[i]['counts'], dtype=np.uint32)
# time for malloc can be saved here but it's fine
data = <uint*> malloc(len(cnts)* sizeof(uint))
for j in range(len(cnts)):
data[j] = <uint> cnts[j]
R = RLE(ucRles[i]['size'][0], ucRles[i]['size'][1], len(cnts), <uint*> data)
Rs._R[0] = R
objs.append(_toString(Rs)[0])
return objs
def frPyObjects(pyobj, h, w):
# encode rle from a list of python objects
if type(pyobj) == np.ndarray:
objs = frBbox(pyobj, h, w)
elif type(pyobj) == list and len(pyobj[0]) == 4:
objs = frBbox(pyobj, h, w)
elif type(pyobj) == list and len(pyobj[0]) > 4:
objs = frPoly(pyobj, h, w)
elif type(pyobj) == list and type(pyobj[0]) == dict \
and 'counts' in pyobj[0] and 'size' in pyobj[0]:
objs = frUncompressedRLE(pyobj, h, w)
# encode rle from single python object
elif type(pyobj) == list and len(pyobj) == 4:
objs = frBbox([pyobj], h, w)[0]
elif type(pyobj) == list and len(pyobj) > 4:
objs = frPoly([pyobj], h, w)[0]
elif type(pyobj) == dict and 'counts' in pyobj and 'size' in pyobj:
objs = frUncompressedRLE([pyobj], h, w)[0]
else:
raise Exception('input type is not supported.')
return objs
import json
import time
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
from matplotlib.patches import Polygon
import numpy as np
import copy
import itertools
import os
import os.path as osp
from collections import defaultdict
import sys
from datetime import datetime
def _isArrayLike(obj):
return hasattr(obj, "__iter__") and hasattr(obj, "__len__")
class COCO:
def __init__(self, annotation_file=None):
"""
Constructor of Microsoft COCO helper class for reading and visualizing annotations.
:param annotation_file (str): location of annotation file
:param image_folder (str): location to the folder that hosts images.
:return:
"""
# dataset, anns, cats, imgs, imgToAnns, catToImgs, imgNameToId, maxAnnId, maxImgId
self.dataset = {
"categories": [],
"images": [],
"annotations": [],
"info": "",
"licenses": [],
} # the complete json
self.anns = dict() # anns[annId]={}
self.cats = dict() # cats[catId] = {}
self.imgs = dict() # imgs[imgId] = {}
self.imgToAnns = defaultdict(list) # imgToAnns[imgId] = [ann]
self.catToImgs = defaultdict(list) # catToImgs[catId] = [imgId]
self.imgNameToId = defaultdict(list) # imgNameToId[name] = imgId
self.maxAnnId = 0
self.maxImgId = 0
if annotation_file is not None and osp.exists(annotation_file):
print("loading annotations into memory...")
tic = time.time()
dataset = json.load(open(annotation_file, "r"))
assert (
type(dataset) == dict
), "annotation file format {} not supported".format(type(dataset))
print("Done (t={:0.2f}s)".format(time.time() - tic))
self.dataset = dataset
self.createIndex()
print(
f"load coco with {len(self.dataset['images'])} images and {len(self.dataset['annotations'])} annotations."
)
def hasImage(self, imageName):
imgId = self.imgNameToId.get(imageName, None)
return imgId is not None
def hasCat(self, catIdx):
res = self.cats.get(catIdx)
return res is not None
def createIndex(self):
# create index
print("creating index...")
anns, cats, imgs = {}, {}, {}
imgNameToId, imgToAnns, catToImgs, imgNameToId = [
defaultdict(list) for _ in range(4)
]
if "annotations" in self.dataset:
for ann in self.dataset["annotations"]:
imgToAnns[ann["image_id"]].append(ann)
anns[ann["id"]] = ann
self.maxAnnId = max(self.maxAnnId, ann["id"])
if "images" in self.dataset:
for img in self.dataset["images"]:
imgs[img["id"]] = img
imgNameToId[img["file_name"]] = img["id"]
try:
imgId = int(img["id"])
self.maxImgId = max(self.maxImgId, imgId)
except:
pass
if "categories" in self.dataset:
for cat in self.dataset["categories"]:
cats[cat["id"]] = cat
if "annotations" in self.dataset and "categories" in self.dataset:
for ann in self.dataset["annotations"]:
catToImgs[ann["category_id"]].append(ann["image_id"])
# TODO: read license
print("index created!")
self.anns = anns
self.imgToAnns = imgToAnns
self.catToImgs = catToImgs
self.imgNameToId = imgNameToId
self.imgs = imgs
self.cats = cats
def setInfo(
self,
year: int="",
version: str="",
description: str="",
contributor: str="",
url: str="",
date_created: datetime="", ):
self.dataset["info"] = {
"year": year,
"version": version,
"description": description,
"contributor": contributor,
"url": url,
"date_created": date_created,
}
def addCategory(
self,
id: int,
name: str,
color: list,
supercategory: str="", ):
cat = {
"id": id,
"name": name,
"color": color,
"supercategory": supercategory,
}
self.cats[id] = cat
self.dataset["categories"].append(cat)
def updateCategory(
self,
id: int,
name: str,
color: list,
supercategory: str="", ):
cat = {
"id": id,
"name": name,
"color": color,
"supercategory": supercategory,
}
self.cats[id] = cat
for idx in range(len(self.dataset["categories"])):
if self.dataset["categories"][idx]["id"] == id:
self.dataset["categories"][idx] = cat
def addImage(
self,
file_name: str,
width: int,
height: int,
id: int=None,
license: int="",
flickr_url: str="",
coco_url: str="",
date_captured: datetime="", ):
if self.hasImage(file_name):
print(f"{file_name}图片已存在")
return
if not id:
self.maxImgId += 1
id = self.maxImgId
image = {
"id": id,
"width": width,
"height": height,
"file_name": file_name,
"license": license,
"flickr_url": flickr_url,
"coco_url": coco_url,
"date_captured": date_captured,
}
self.dataset["images"].append(image)
self.imgs[id] = image
self.imgNameToId[file_name] = id
return id
def getBB(self, segmentation):
x = segmentation[::2]
y = segmentation[1::2]
maxx, minx, maxy, miny = max(x), min(x), max(y), min(y)
return [minx, miny, maxx - minx, maxy - miny]
def getArea(self, segmentation):
x = segmentation[::2]
y = segmentation[1::2]
return 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))
def addAnnotation(
self,
image_id: int,
category_id: int,
segmentation: list,
area: float=None,
id: int=None, ):
if id is not None and self.anns.get(id, None) is not None:
print("标签已经存在")
return
if not id:
self.maxAnnId += 1
id = self.maxAnnId
ann = {
"id": id,
"iscrowd": 0,
"image_id": image_id,
"category_id": category_id,
"segmentation": [segmentation],
"area": self.getArea(segmentation),
"bbox": self.getBB(segmentation),
}
self.dataset["annotations"].append(ann)
self.anns[id] = ann
self.imgToAnns[image_id].append(ann)
self.catToImgs[category_id].append(image_id)
return id
def delAnnotation(self, annId, imgId):
if "annotations" in self.dataset:
for idx, ann in enumerate(self.dataset["annotations"]):
if ann["id"] == annId:
del self.dataset["annotations"][idx]
if annId in self.anns.keys():
del self.anns[annId]
for idx, ann in enumerate(self.imgToAnns[imgId]):
if ann["id"] == annId:
del self.imgToAnns[imgId][idx]
def updateAnnotation(self, id, imgId, segmentation):
self.anns[id]["segmentation"] = [segmentation]
self.anns[id]["bbox"] = self.getBB(segmentation)
self.anns[id]["area"] = self.getArea(segmentation)
for rec in self.dataset["annotations"]:
if rec["id"] == id:
rec = self.anns[id]
break
for rec in self.dataset["annotations"]:
if rec["id"] == id:
# @todo TODO move into debug codes or controls
print(
"record point : ",
rec["segmentation"][0][0],
rec["segmentation"][0][1], )
break
for rec in self.imgToAnns[imgId]:
if rec["id"] == id:
rec["segmentation"] = [segmentation]
break
def info(self):
"""
Print information about the annotation file.
:return:
"""
for key, value in self.dataset["info"].items():
print("{}: {}".format(key, value))
def getAnnIds(self, imgIds=[], catIds=[], areaRng=[], iscrowd=None):
"""
Get ann ids that satisfy given filter conditions. default skips that filter
:param imgIds (int array) : get anns for given imgs
catIds (int array) : get anns for given cats
areaRng (float array) : get anns for given area range (e.g. [0 inf])
iscrowd (boolean) : get anns for given crowd label (False or True)
:return: ids (int array) : integer array of ann ids
"""
imgIds = imgIds if _isArrayLike(imgIds) else [imgIds]
catIds = catIds if _isArrayLike(catIds) else [catIds]
if len(imgIds) == len(catIds) == len(areaRng) == 0:
anns = self.dataset["annotations"]
else:
if not len(imgIds) == 0:
lists = [
self.imgToAnns[imgId] for imgId in imgIds
if imgId in self.imgToAnns
]
anns = list(itertools.chain.from_iterable(lists))
else:
anns = self.dataset["annotations"]
anns = (anns if len(catIds) == 0 else
[ann for ann in anns if ann["category_id"] in catIds])
anns = (anns if len(areaRng) == 0 else [
ann for ann in anns
if ann["area"] > areaRng[0] and ann["area"] < areaRng[1]
])
if not iscrowd == None:
ids = [ann["id"] for ann in anns if ann["iscrowd"] == iscrowd]
else:
ids = [ann["id"] for ann in anns]
return ids
def getCatIds(self, catNms=[], supNms=[], catIds=[]):
"""
filtering parameters. default skips that filter.
:param catNms (str array) : get cats for given cat names
:param supNms (str array) : get cats for given supercategory names
:param catIds (int array) : get cats for given cat ids
:return: ids (int array) : integer array of cat ids
"""
catNms = catNms if _isArrayLike(catNms) else [catNms]
supNms = supNms if _isArrayLike(supNms) else [supNms]
catIds = catIds if _isArrayLike(catIds) else [catIds]
if len(catNms) == len(supNms) == len(catIds) == 0:
cats = self.dataset["categories"]
else:
cats = self.dataset["categories"]
cats = (cats if len(catNms) == 0 else
[cat for cat in cats if cat["name"] in catNms])
cats = (cats if len(supNms) == 0 else
[cat for cat in cats if cat["supercategory"] in supNms])
cats = (cats if len(catIds) == 0 else
[cat for cat in cats if cat["id"] in catIds])
ids = [cat["id"] for cat in cats]
return ids
def getImgIds(self, imgIds=[], catIds=[]):
"""
Get img ids that satisfy given filter conditions.
:param imgIds (int array) : get imgs for given ids
:param catIds (int array) : get imgs with all given cats
:return: ids (int array) : integer array of img ids
"""
imgIds = imgIds if _isArrayLike(imgIds) else [imgIds]
catIds = catIds if _isArrayLike(catIds) else [catIds]
if len(imgIds) == len(catIds) == 0:
ids = self.imgs.keys()
else:
ids = set(imgIds)
for i, catId in enumerate(catIds):
if i == 0 and len(ids) == 0:
ids = set(self.catToImgs[catId])
else:
ids &= set(self.catToImgs[catId])
return list(ids)
def loadAnns(self, ids=[]):
"""
Load anns with the specified ids.
:param ids (int array) : integer ids specifying anns
:return: anns (object array) : loaded ann objects
"""
if _isArrayLike(ids):
return [self.anns[id] for id in ids]
elif type(ids) == int:
return [self.anns[ids]]
def loadCats(self, ids=[]):
"""
Load cats with the specified ids.
:param ids (int array) : integer ids specifying cats
:return: cats (object array) : loaded cat objects
"""
if _isArrayLike(ids):
return [self.cats[id] for id in ids]
elif type(ids) == int:
return [self.cats[ids]]
def loadImgs(self, ids=[]):
"""
Load anns with the specified ids.
:param ids (int array) : integer ids specifying img
:return: imgs (object array) : loaded img objects
"""
if _isArrayLike(ids):
return [self.imgs[id] for id in ids]
elif type(ids) == int:
return [self.imgs[ids]]
# def showAnns(self, anns, draw_bbox=False):
# """
# Display the specified annotations.
# :param anns (array of object): annotations to display
# :return: None
# """
# if len(anns) == 0:
# return 0
# if "segmentation" in anns[0] or "keypoints" in anns[0]:
# datasetType = "instances"
# elif "caption" in anns[0]:
# datasetType = "captions"
# else:
# raise Exception("datasetType not supported")
# if datasetType == "instances":
# ax = plt.gca()
# ax.set_autoscale_on(False)
# polygons = []
# color = []
# for ann in anns:
# c = (np.random.random((1, 3)) * 0.6 + 0.4).tolist()[0]
# if "segmentation" in ann:
# if type(ann["segmentation"]) == list:
# # polygon
# for seg in ann["segmentation"]:
# poly = np.array(seg).reshape((int(len(seg) / 2), 2))
# polygons.append(Polygon(poly))
# color.append(c)
# else:
# # mask
# t = self.imgs[ann["image_id"]]
# if type(ann["segmentation"]["counts"]) == list:
# rle = maskUtils.frPyObjects(
# [ann["segmentation"]], t["height"], t["width"]
# )
# else:
# rle = [ann["segmentation"]]
# m = maskUtils.decode(rle)
# img = np.ones((m.shape[0], m.shape[1], 3))
# if ann["iscrowd"] == 1:
# color_mask = np.array([2.0, 166.0, 101.0]) / 255
# if ann["iscrowd"] == 0:
# color_mask = np.random.random((1, 3)).tolist()[0]
# for i in range(3):
# img[:, :, i] = color_mask[i]
# ax.imshow(np.dstack((img, m * 0.5)))
# if "keypoints" in ann and type(ann["keypoints"]) == list:
# # turn skeleton into zero-based index
# sks = np.array(self.loadCats(ann["category_id"])[0]["skeleton"]) - 1
# kp = np.array(ann["keypoints"])
# x = kp[0::3]
# y = kp[1::3]
# v = kp[2::3]
# for sk in sks:
# if np.all(v[sk] > 0):
# plt.plot(x[sk], y[sk], linewidth=3, color=c)
# plt.plot(
# x[v > 0],
# y[v > 0],
# "o",
# markersize=8,
# markerfacecolor=c,
# markeredgecolor="k",
# markeredgewidth=2,
# )
# plt.plot(
# x[v > 1],
# y[v > 1],
# "o",
# markersize=8,
# markerfacecolor=c,
# markeredgecolor=c,
# markeredgewidth=2,
# )
#
# if draw_bbox:
# [bbox_x, bbox_y, bbox_w, bbox_h] = ann["bbox"]
# poly = [
# [bbox_x, bbox_y],
# [bbox_x, bbox_y + bbox_h],
# [bbox_x + bbox_w, bbox_y + bbox_h],
# [bbox_x + bbox_w, bbox_y],
# ]
# np_poly = np.array(poly).reshape((4, 2))
# polygons.append(Polygon(np_poly))
# color.append(c)
#
# p = PatchCollection(polygons, facecolor=color, linewidths=0, alpha=0.4)
# ax.add_collection(p)
# p = PatchCollection(
# polygons, facecolor="none", edgecolors=color, linewidths=2
# )
# ax.add_collection(p)
# elif datasetType == "captions":
# for ann in anns:
# print(ann["caption"])
#
# def loadRes(self, resFile):
# """
# Load result file and return a result api object.
# :param resFile (str) : file name of result file
# :return: res (obj) : result api object
# """
# res = COCO()
# res.dataset["images"] = [img for img in self.dataset["images"]]
#
# print("Loading and preparing results...")
# tic = time.time()
# if type(resFile) == str or (PYTHON_VERSION == 2 and type(resFile) == unicode):
# anns = json.load(open(resFile))
# elif type(resFile) == np.ndarray:
# anns = self.loadNumpyAnnotations(resFile)
# else:
# anns = resFile
# assert type(anns) == list, "results in not an array of objects"
# annsImgIds = [ann["image_id"] for ann in anns]
# assert set(annsImgIds) == (
# set(annsImgIds) & set(self.getImgIds())
# ), "Results do not correspond to current coco set"
# if "caption" in anns[0]:
# imgIds = set([img["id"] for img in res.dataset["images"]]) & set(
# [ann["image_id"] for ann in anns]
# )
# res.dataset["images"] = [
# img for img in res.dataset["images"] if img["id"] in imgIds
# ]
# for id, ann in enumerate(anns):
# ann["id"] = id + 1
# elif "bbox" in anns[0] and not anns[0]["bbox"] == []:
# res.dataset["categories"] = copy.deepcopy(self.dataset["categories"])
# for id, ann in enumerate(anns):
# bb = ann["bbox"]
# x1, x2, y1, y2 = [bb[0], bb[0] + bb[2], bb[1], bb[1] + bb[3]]
# if not "segmentation" in ann:
# ann["segmentation"] = [[x1, y1, x1, y2, x2, y2, x2, y1]]
# ann["area"] = bb[2] * bb[3]
# ann["id"] = id + 1
# ann["iscrowd"] = 0
# elif "segmentation" in anns[0]:
# res.dataset["categories"] = copy.deepcopy(self.dataset["categories"])
# for id, ann in enumerate(anns):
# # now only support compressed RLE format as segmentation results
# ann["area"] = maskUtils.area(ann["segmentation"])
# if not "bbox" in ann:
# ann["bbox"] = maskUtils.toBbox(ann["segmentation"])
# ann["id"] = id + 1
# ann["iscrowd"] = 0
# elif "keypoints" in anns[0]:
# res.dataset["categories"] = copy.deepcopy(self.dataset["categories"])
# for id, ann in enumerate(anns):
# s = ann["keypoints"]
# x = s[0::3]
# y = s[1::3]
# x0, x1, y0, y1 = np.min(x), np.max(x), np.min(y), np.max(y)
# ann["area"] = (x1 - x0) * (y1 - y0)
# ann["id"] = id + 1
# ann["bbox"] = [x0, y0, x1 - x0, y1 - y0]
# print("DONE (t={:0.2f}s)".format(time.time() - tic))
#
# res.dataset["annotations"] = anns
# res.createIndex()
# return res
def download(self, tarDir=None, imgIds=[]):
"""
Download COCO images from mscoco.org server.
:param tarDir (str): COCO results directory name
imgIds (list): images to be downloaded
:return:
"""
if tarDir is None:
print("Please specify target directory")
return -1
if len(imgIds) == 0:
imgs = self.imgs.values()
else:
imgs = self.loadImgs(imgIds)
N = len(imgs)
if not os.path.exists(tarDir):
os.makedirs(tarDir)
for i, img in enumerate(imgs):
tic = time.time()
fname = os.path.join(tarDir, img["file_name"])
if not os.path.exists(fname):
urlretrieve(img["coco_url"], fname)
print("downloaded {}/{} images (t={:0.1f}s)".format(
i, N, time.time() - tic))
def loadNumpyAnnotations(self, data):
"""
Convert result data from a numpy array [Nx7] where each row contains {imageID,x1,y1,w,h,score,class}
:param data (numpy.ndarray)
:return: annotations (python nested list)
"""
print("Converting ndarray to lists...")
assert type(data) == np.ndarray
print(data.shape)
assert data.shape[1] == 7
N = data.shape[0]
ann = []
for i in range(N):
if i % 1000000 == 0:
print("{}/{}".format(i, N))
ann += [{
"image_id": int(data[i, 0]),
"bbox": [data[i, 1], data[i, 2], data[i, 3], data[i, 4]],
"score": data[i, 5],
"category_id": int(data[i, 6]),
}]
return ann
# def annToRLE(self, ann):
# """
# Convert annotation which can be polygons, uncompressed RLE to RLE.
# :return: binary mask (numpy 2D array)
# """
# t = self.imgs[ann["image_id"]]
# h, w = t["height"], t["width"]
# segm = ann["segmentation"]
# if type(segm) == list:
# # polygon -- a single object might consist of multiple parts
# # we merge all parts into one mask rle code
# rles = maskUtils.frPyObjects(segm, h, w)
# rle = maskUtils.merge(rles)
# elif type(segm["counts"]) == list:
# # uncompressed RLE
# rle = maskUtils.frPyObjects(segm, h, w)
# else:
# # rle
# rle = ann["segmentation"]
# return rle
# def annToMask(self, ann):
# """
# Convert annotation which can be polygons, uncompressed RLE, or RLE to binary mask.
# :return: binary mask (numpy 2D array)
# """
# rle = self.annToRLE(ann)
# m = maskUtils.decode(rle)
# return m
__author__ = 'tsungyi'
import numpy as np
import datetime
import time
from collections import defaultdict
from . import mask as maskUtils
import copy
class COCOeval:
# Interface for evaluating detection on the Microsoft COCO dataset.
#
# The usage for CocoEval is as follows:
# cocoGt=..., cocoDt=... # load dataset and results
# E = CocoEval(cocoGt,cocoDt); # initialize CocoEval object
# E.params.recThrs = ...; # set parameters as desired
# E.evaluate(); # run per image evaluation
# E.accumulate(); # accumulate per image results
# E.summarize(); # display summary metrics of results
# For example usage see evalDemo.m and http://mscoco.org/.
#
# The evaluation parameters are as follows (defaults in brackets):
# imgIds - [all] N img ids to use for evaluation
# catIds - [all] K cat ids to use for evaluation
# iouThrs - [.5:.05:.95] T=10 IoU thresholds for evaluation
# recThrs - [0:.01:1] R=101 recall thresholds for evaluation
# areaRng - [...] A=4 object area ranges for evaluation
# maxDets - [1 10 100] M=3 thresholds on max detections per image
# iouType - ['segm'] set iouType to 'segm', 'bbox' or 'keypoints'
# iouType replaced the now DEPRECATED useSegm parameter.
# useCats - [1] if true use category labels for evaluation
# Note: if useCats=0 category labels are ignored as in proposal scoring.
# Note: multiple areaRngs [Ax2] and maxDets [Mx1] can be specified.
#
# evaluate(): evaluates detections on every image and every category and
# concats the results into the "evalImgs" with fields:
# dtIds - [1xD] id for each of the D detections (dt)
# gtIds - [1xG] id for each of the G ground truths (gt)
# dtMatches - [TxD] matching gt id at each IoU or 0
# gtMatches - [TxG] matching dt id at each IoU or 0
# dtScores - [1xD] confidence of each dt
# gtIgnore - [1xG] ignore flag for each gt
# dtIgnore - [TxD] ignore flag for each dt at each IoU
#
# accumulate(): accumulates the per-image, per-category evaluation
# results in "evalImgs" into the dictionary "eval" with fields:
# params - parameters used for evaluation
# date - date evaluation was performed
# counts - [T,R,K,A,M] parameter dimensions (see above)
# precision - [TxRxKxAxM] precision for every evaluation setting
# recall - [TxKxAxM] max recall for every evaluation setting
# Note: precision and recall==-1 for settings with no gt objects.
#
# See also coco, mask, pycocoDemo, pycocoEvalDemo
#
# Microsoft COCO Toolbox. version 2.0
# Data, paper, and tutorials available at: http://mscoco.org/
# Code written by Piotr Dollar and Tsung-Yi Lin, 2015.
# Licensed under the Simplified BSD License [see coco/license.txt]
def __init__(self, cocoGt=None, cocoDt=None, iouType='segm'):
'''
Initialize CocoEval using coco APIs for gt and dt
:param cocoGt: coco object with ground truth annotations
:param cocoDt: coco object with detection results
:return: None
'''
if not iouType:
print('iouType not specified. use default iouType segm')
self.cocoGt = cocoGt # ground truth COCO API
self.cocoDt = cocoDt # detections COCO API
self.evalImgs = defaultdict(
list) # per-image per-category evaluation results [KxAxI] elements
self.eval = {} # accumulated evaluation results
self._gts = defaultdict(list) # gt for evaluation
self._dts = defaultdict(list) # dt for evaluation
self.params = Params(iouType=iouType) # parameters
self._paramsEval = {} # parameters for evaluation
self.stats = [] # result summarization
self.ious = {} # ious between all gts and dts
if not cocoGt is None:
self.params.imgIds = sorted(cocoGt.getImgIds())
self.params.catIds = sorted(cocoGt.getCatIds())
def _prepare(self):
'''
Prepare ._gts and ._dts for evaluation based on params
:return: None
'''
def _toMask(anns, coco):
# modify ann['segmentation'] by reference
for ann in anns:
rle = coco.annToRLE(ann)
ann['segmentation'] = rle
p = self.params
if p.useCats:
gts = self.cocoGt.loadAnns(
self.cocoGt.getAnnIds(
imgIds=p.imgIds, catIds=p.catIds))
dts = self.cocoDt.loadAnns(
self.cocoDt.getAnnIds(
imgIds=p.imgIds, catIds=p.catIds))
else:
gts = self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds))
dts = self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds))
# convert ground truth to mask if iouType == 'segm'
if p.iouType == 'segm':
_toMask(gts, self.cocoGt)
_toMask(dts, self.cocoDt)
# set ignore flag
for gt in gts:
gt['ignore'] = gt['ignore'] if 'ignore' in gt else 0
gt['ignore'] = 'iscrowd' in gt and gt['iscrowd']
if p.iouType == 'keypoints':
gt['ignore'] = (gt['num_keypoints'] == 0) or gt['ignore']
self._gts = defaultdict(list) # gt for evaluation
self._dts = defaultdict(list) # dt for evaluation
for gt in gts:
self._gts[gt['image_id'], gt['category_id']].append(gt)
for dt in dts:
self._dts[dt['image_id'], dt['category_id']].append(dt)
self.evalImgs = defaultdict(
list) # per-image per-category evaluation results
self.eval = {} # accumulated evaluation results
def evaluate(self):
'''
Run per image evaluation on given images and store results (a list of dict) in self.evalImgs
:return: None
'''
tic = time.time()
print('Running per image evaluation...')
p = self.params
# add backward compatibility if useSegm is specified in params
if not p.useSegm is None:
p.iouType = 'segm' if p.useSegm == 1 else 'bbox'
print('useSegm (deprecated) is not None. Running {} evaluation'.
format(p.iouType))
print('Evaluate annotation type *{}*'.format(p.iouType))
p.imgIds = list(np.unique(p.imgIds))
if p.useCats:
p.catIds = list(np.unique(p.catIds))
p.maxDets = sorted(p.maxDets)
self.params = p
self._prepare()
# loop through images, area range, max detection number
catIds = p.catIds if p.useCats else [-1]
if p.iouType == 'segm' or p.iouType == 'bbox':
computeIoU = self.computeIoU
elif p.iouType == 'keypoints':
computeIoU = self.computeOks
self.ious = {(imgId, catId): computeIoU(imgId, catId) \
for imgId in p.imgIds
for catId in catIds}
evaluateImg = self.evaluateImg
maxDet = p.maxDets[-1]
self.evalImgs = [
evaluateImg(imgId, catId, areaRng, maxDet)
for catId in catIds for areaRng in p.areaRng for imgId in p.imgIds
]
self._paramsEval = copy.deepcopy(self.params)
toc = time.time()
print('DONE (t={:0.2f}s).'.format(toc - tic))
def computeIoU(self, imgId, catId):
p = self.params
if p.useCats:
gt = self._gts[imgId, catId]
dt = self._dts[imgId, catId]
else:
gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]]
dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]]
if len(gt) == 0 and len(dt) == 0:
return []
inds = np.argsort([-d['score'] for d in dt], kind='mergesort')
dt = [dt[i] for i in inds]
if len(dt) > p.maxDets[-1]:
dt = dt[0:p.maxDets[-1]]
if p.iouType == 'segm':
g = [g['segmentation'] for g in gt]
d = [d['segmentation'] for d in dt]
elif p.iouType == 'bbox':
g = [g['bbox'] for g in gt]
d = [d['bbox'] for d in dt]
else:
raise Exception('unknown iouType for iou computation')
# compute iou between each dt and gt region
iscrowd = [int(o['iscrowd']) for o in gt]
ious = maskUtils.iou(d, g, iscrowd)
return ious
def computeOks(self, imgId, catId):
p = self.params
# dimention here should be Nxm
gts = self._gts[imgId, catId]
dts = self._dts[imgId, catId]
inds = np.argsort([-d['score'] for d in dts], kind='mergesort')
dts = [dts[i] for i in inds]
if len(dts) > p.maxDets[-1]:
dts = dts[0:p.maxDets[-1]]
# if len(gts) == 0 and len(dts) == 0:
if len(gts) == 0 or len(dts) == 0:
return []
ious = np.zeros((len(dts), len(gts)))
sigmas = p.kpt_oks_sigmas
vars = (sigmas * 2)**2
k = len(sigmas)
# compute oks between each detection and ground truth object
for j, gt in enumerate(gts):
# create bounds for ignore regions(double the gt bbox)
g = np.array(gt['keypoints'])
xg = g[0::3]
yg = g[1::3]
vg = g[2::3]
k1 = np.count_nonzero(vg > 0)
bb = gt['bbox']
x0 = bb[0] - bb[2]
x1 = bb[0] + bb[2] * 2
y0 = bb[1] - bb[3]
y1 = bb[1] + bb[3] * 2
for i, dt in enumerate(dts):
d = np.array(dt['keypoints'])
xd = d[0::3]
yd = d[1::3]
if k1 > 0:
# measure the per-keypoint distance if keypoints visible
dx = xd - xg
dy = yd - yg
else:
# measure minimum distance to keypoints in (x0,y0) & (x1,y1)
z = np.zeros((k))
dx = np.max((z, x0 - xd), axis=0) + np.max(
(z, xd - x1), axis=0)
dy = np.max((z, y0 - yd), axis=0) + np.max(
(z, yd - y1), axis=0)
e = (dx**2 + dy**2) / vars / (gt['area'] + np.spacing(1)) / 2
if k1 > 0:
e = e[vg > 0]
ious[i, j] = np.sum(np.exp(-e)) / e.shape[0]
return ious
def evaluateImg(self, imgId, catId, aRng, maxDet):
'''
perform evaluation for single category and image
:return: dict (single image results)
'''
p = self.params
if p.useCats:
gt = self._gts[imgId, catId]
dt = self._dts[imgId, catId]
else:
gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]]
dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]]
if len(gt) == 0 and len(dt) == 0:
return None
for g in gt:
if g['ignore'] or (g['area'] < aRng[0] or g['area'] > aRng[1]):
g['_ignore'] = 1
else:
g['_ignore'] = 0
# sort dt highest score first, sort gt ignore last
gtind = np.argsort([g['_ignore'] for g in gt], kind='mergesort')
gt = [gt[i] for i in gtind]
dtind = np.argsort([-d['score'] for d in dt], kind='mergesort')
dt = [dt[i] for i in dtind[0:maxDet]]
iscrowd = [int(o['iscrowd']) for o in gt]
# load computed ious
ious = self.ious[imgId, catId][:, gtind] if len(self.ious[
imgId, catId]) > 0 else self.ious[imgId, catId]
T = len(p.iouThrs)
G = len(gt)
D = len(dt)
gtm = np.zeros((T, G))
dtm = np.zeros((T, D))
gtIg = np.array([g['_ignore'] for g in gt])
dtIg = np.zeros((T, D))
if not len(ious) == 0:
for tind, t in enumerate(p.iouThrs):
for dind, d in enumerate(dt):
# information about best match so far (m=-1 -> unmatched)
iou = min([t, 1 - 1e-10])
m = -1
for gind, g in enumerate(gt):
# if this gt already matched, and not a crowd, continue
if gtm[tind, gind] > 0 and not iscrowd[gind]:
continue
# if dt matched to reg gt, and on ignore gt, stop
if m > -1 and gtIg[m] == 0 and gtIg[gind] == 1:
break
# continue to next gt unless better match made
if ious[dind, gind] < iou:
continue
# if match successful and best so far, store appropriately
iou = ious[dind, gind]
m = gind
# if match made store id of match for both dt and gt
if m == -1:
continue
dtIg[tind, dind] = gtIg[m]
dtm[tind, dind] = gt[m]['id']
gtm[tind, m] = d['id']
# set unmatched detections outside of area range to ignore
a = np.array(
[d['area'] < aRng[0] or d['area'] > aRng[1] for d in dt]).reshape(
(1, len(dt)))
dtIg = np.logical_or(dtIg, np.logical_and(dtm == 0, np.repeat(a, T, 0)))
# store results for given image and category
return {
'image_id': imgId,
'category_id': catId,
'aRng': aRng,
'maxDet': maxDet,
'dtIds': [d['id'] for d in dt],
'gtIds': [g['id'] for g in gt],
'dtMatches': dtm,
'gtMatches': gtm,
'dtScores': [d['score'] for d in dt],
'gtIgnore': gtIg,
'dtIgnore': dtIg,
}
def accumulate(self, p=None):
'''
Accumulate per image evaluation results and store the result in self.eval
:param p: input params for evaluation
:return: None
'''
print('Accumulating evaluation results...')
tic = time.time()
if not self.evalImgs:
print('Please run evaluate() first')
# allows input customized parameters
if p is None:
p = self.params
p.catIds = p.catIds if p.useCats == 1 else [-1]
T = len(p.iouThrs)
R = len(p.recThrs)
K = len(p.catIds) if p.useCats else 1
A = len(p.areaRng)
M = len(p.maxDets)
precision = -np.ones(
(T, R, K, A, M)) # -1 for the precision of absent categories
recall = -np.ones((T, K, A, M))
scores = -np.ones((T, R, K, A, M))
# create dictionary for future indexing
_pe = self._paramsEval
catIds = _pe.catIds if _pe.useCats else [-1]
setK = set(catIds)
setA = set(map(tuple, _pe.areaRng))
setM = set(_pe.maxDets)
setI = set(_pe.imgIds)
# get inds to evaluate
k_list = [n for n, k in enumerate(p.catIds) if k in setK]
m_list = [m for n, m in enumerate(p.maxDets) if m in setM]
a_list = [
n for n, a in enumerate(map(lambda x: tuple(x), p.areaRng))
if a in setA
]
i_list = [n for n, i in enumerate(p.imgIds) if i in setI]
I0 = len(_pe.imgIds)
A0 = len(_pe.areaRng)
# retrieve E at each category, area range, and max number of detections
for k, k0 in enumerate(k_list):
Nk = k0 * A0 * I0
for a, a0 in enumerate(a_list):
Na = a0 * I0
for m, maxDet in enumerate(m_list):
E = [self.evalImgs[Nk + Na + i] for i in i_list]
E = [e for e in E if not e is None]
if len(E) == 0:
continue
dtScores = np.concatenate(
[e['dtScores'][0:maxDet] for e in E])
# different sorting method generates slightly different results.
# mergesort is used to be consistent as Matlab implementation.
inds = np.argsort(-dtScores, kind='mergesort')
dtScoresSorted = dtScores[inds]
dtm = np.concatenate(
[e['dtMatches'][:, 0:maxDet] for e in E],
axis=1)[:, inds]
dtIg = np.concatenate(
[e['dtIgnore'][:, 0:maxDet] for e in E],
axis=1)[:, inds]
gtIg = np.concatenate([e['gtIgnore'] for e in E])
npig = np.count_nonzero(gtIg == 0)
if npig == 0:
continue
tps = np.logical_and(dtm, np.logical_not(dtIg))
fps = np.logical_and(
np.logical_not(dtm), np.logical_not(dtIg))
tp_sum = np.cumsum(tps, axis=1).astype(dtype=np.float)
fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float)
for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)):
tp = np.array(tp)
fp = np.array(fp)
nd = len(tp)
rc = tp / npig
pr = tp / (fp + tp + np.spacing(1))
q = np.zeros((R, ))
ss = np.zeros((R, ))
if nd:
recall[t, k, a, m] = rc[-1]
else:
recall[t, k, a, m] = 0
# numpy is slow without cython optimization for accessing elements
# use python array gets significant speed improvement
pr = pr.tolist()
q = q.tolist()
for i in range(nd - 1, 0, -1):
if pr[i] > pr[i - 1]:
pr[i - 1] = pr[i]
inds = np.searchsorted(rc, p.recThrs, side='left')
try:
for ri, pi in enumerate(inds):
q[ri] = pr[pi]
ss[ri] = dtScoresSorted[pi]
except:
pass
precision[t, :, k, a, m] = np.array(q)
scores[t, :, k, a, m] = np.array(ss)
self.eval = {
'params': p,
'counts': [T, R, K, A, M],
'date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'precision': precision,
'recall': recall,
'scores': scores,
}
toc = time.time()
print('DONE (t={:0.2f}s).'.format(toc - tic))
def summarize(self):
'''
Compute and display summary metrics for evaluation results.
Note this functin can *only* be applied on the default parameter setting
'''
def _summarize(ap=1, iouThr=None, areaRng='all', maxDets=100):
p = self.params
iStr = ' {:<18} {} @[ IoU={:<9} | area={:>6s} | maxDets={:>3d} ] = {:0.3f}'
titleStr = 'Average Precision' if ap == 1 else 'Average Recall'
typeStr = '(AP)' if ap == 1 else '(AR)'
iouStr = '{:0.2f}:{:0.2f}'.format(p.iouThrs[0], p.iouThrs[-1]) \
if iouThr is None else '{:0.2f}'.format(iouThr)
aind = [i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng]
mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets]
if ap == 1:
# dimension of precision: [TxRxKxAxM]
s = self.eval['precision']
# IoU
if iouThr is not None:
t = np.where(iouThr == p.iouThrs)[0]
s = s[t]
s = s[:, :, :, aind, mind]
else:
# dimension of recall: [TxKxAxM]
s = self.eval['recall']
if iouThr is not None:
t = np.where(iouThr == p.iouThrs)[0]
s = s[t]
s = s[:, :, aind, mind]
if len(s[s > -1]) == 0:
mean_s = -1
else:
mean_s = np.mean(s[s > -1])
print(
iStr.format(titleStr, typeStr, iouStr, areaRng, maxDets,
mean_s))
return mean_s
def _summarizeDets():
stats = np.zeros((12, ))
stats[0] = _summarize(1)
stats[1] = _summarize(1, iouThr=.5, maxDets=self.params.maxDets[2])
stats[2] = _summarize(1, iouThr=.75, maxDets=self.params.maxDets[2])
stats[3] = _summarize(
1, areaRng='small', maxDets=self.params.maxDets[2])
stats[4] = _summarize(
1, areaRng='medium', maxDets=self.params.maxDets[2])
stats[5] = _summarize(
1, areaRng='large', maxDets=self.params.maxDets[2])
stats[6] = _summarize(0, maxDets=self.params.maxDets[0])
stats[7] = _summarize(0, maxDets=self.params.maxDets[1])
stats[8] = _summarize(0, maxDets=self.params.maxDets[2])
stats[9] = _summarize(
0, areaRng='small', maxDets=self.params.maxDets[2])
stats[10] = _summarize(
0, areaRng='medium', maxDets=self.params.maxDets[2])
stats[11] = _summarize(
0, areaRng='large', maxDets=self.params.maxDets[2])
return stats
def _summarizeKps():
stats = np.zeros((10, ))
stats[0] = _summarize(1, maxDets=20)
stats[1] = _summarize(1, maxDets=20, iouThr=.5)
stats[2] = _summarize(1, maxDets=20, iouThr=.75)
stats[3] = _summarize(1, maxDets=20, areaRng='medium')
stats[4] = _summarize(1, maxDets=20, areaRng='large')
stats[5] = _summarize(0, maxDets=20)
stats[6] = _summarize(0, maxDets=20, iouThr=.5)
stats[7] = _summarize(0, maxDets=20, iouThr=.75)
stats[8] = _summarize(0, maxDets=20, areaRng='medium')
stats[9] = _summarize(0, maxDets=20, areaRng='large')
return stats
if not self.eval:
raise Exception('Please run accumulate() first')
iouType = self.params.iouType
if iouType == 'segm' or iouType == 'bbox':
summarize = _summarizeDets
elif iouType == 'keypoints':
summarize = _summarizeKps
self.stats = summarize()
def __str__(self):
self.summarize()
class Params:
'''
Params for coco evaluation api
'''
def setDetParams(self):
self.imgIds = []
self.catIds = []
# np.arange causes trouble. the data point on arange is slightly larger than the true value
self.iouThrs = np.linspace(
.5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True)
self.recThrs = np.linspace(
.0, 1.00, int(np.round((1.00 - .0) / .01)) + 1, endpoint=True)
self.maxDets = [1, 10, 100]
self.areaRng = [[0**2, 1e5**2], [0**2, 32**2], [32**2, 96**2],
[96**2, 1e5**2]]
self.areaRngLbl = ['all', 'small', 'medium', 'large']
self.useCats = 1
def setKpParams(self):
self.imgIds = []
self.catIds = []
# np.arange causes trouble. the data point on arange is slightly larger than the true value
self.iouThrs = np.linspace(
.5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True)
self.recThrs = np.linspace(
.0, 1.00, int(np.round((1.00 - .0) / .01)) + 1, endpoint=True)
self.maxDets = [20]
self.areaRng = [[0**2, 1e5**2], [32**2, 96**2], [96**2, 1e5**2]]
self.areaRngLbl = ['all', 'medium', 'large']
self.useCats = 1
self.kpt_oks_sigmas = np.array([
.26, .25, .25, .35, .35, .79, .79, .72, .72, .62, .62, 1.07, 1.07,
.87, .87, .89, .89
]) / 10.0
def __init__(self, iouType='segm'):
if iouType == 'segm' or iouType == 'bbox':
self.setDetParams()
elif iouType == 'keypoints':
self.setKpParams()
else:
raise Exception('iouType not supported')
self.iouType = iouType
# useSegm is deprecated
self.useSegm = None
// https://github.com/vivkin/gason - pulled January 10, 2016
#include "gason.h"
#include <stdlib.h>
#define JSON_ZONE_SIZE 4096
#define JSON_STACK_SIZE 32
const char *jsonStrError(int err) {
switch (err) {
#define XX(no, str) \
case JSON_##no: \
return str;
JSON_ERRNO_MAP(XX)
#undef XX
default:
return "unknown";
}
}
void *JsonAllocator::allocate(size_t size) {
size = (size + 7) & ~7;
if (head && head->used + size <= JSON_ZONE_SIZE) {
char *p = (char *)head + head->used;
head->used += size;
return p;
}
size_t allocSize = sizeof(Zone) + size;
Zone *zone = (Zone *)malloc(allocSize <= JSON_ZONE_SIZE ? JSON_ZONE_SIZE : allocSize);
if (zone == nullptr)
return nullptr;
zone->used = allocSize;
if (allocSize <= JSON_ZONE_SIZE || head == nullptr) {
zone->next = head;
head = zone;
} else {
zone->next = head->next;
head->next = zone;
}
return (char *)zone + sizeof(Zone);
}
void JsonAllocator::deallocate() {
while (head) {
Zone *next = head->next;
free(head);
head = next;
}
}
static inline bool isspace(char c) {
return c == ' ' || (c >= '\t' && c <= '\r');
}
static inline bool isdelim(char c) {
return c == ',' || c == ':' || c == ']' || c == '}' || isspace(c) || !c;
}
static inline bool isdigit(char c) {
return c >= '0' && c <= '9';
}
static inline bool isxdigit(char c) {
return (c >= '0' && c <= '9') || ((c & ~' ') >= 'A' && (c & ~' ') <= 'F');
}
static inline int char2int(char c) {
if (c <= '9')
return c - '0';
return (c & ~' ') - 'A' + 10;
}
static double string2double(char *s, char **endptr) {
char ch = *s;
if (ch == '-')
++s;
double result = 0;
while (isdigit(*s))
result = (result * 10) + (*s++ - '0');
if (*s == '.') {
++s;
double fraction = 1;
while (isdigit(*s)) {
fraction *= 0.1;
result += (*s++ - '0') * fraction;
}
}
if (*s == 'e' || *s == 'E') {
++s;
double base = 10;
if (*s == '+')
++s;
else if (*s == '-') {
++s;
base = 0.1;
}
unsigned int exponent = 0;
while (isdigit(*s))
exponent = (exponent * 10) + (*s++ - '0');
double power = 1;
for (; exponent; exponent >>= 1, base *= base)
if (exponent & 1)
power *= base;
result *= power;
}
*endptr = s;
return ch == '-' ? -result : result;
}
static inline JsonNode *insertAfter(JsonNode *tail, JsonNode *node) {
if (!tail)
return node->next = node;
node->next = tail->next;
tail->next = node;
return node;
}
static inline JsonValue listToValue(JsonTag tag, JsonNode *tail) {
if (tail) {
auto head = tail->next;
tail->next = nullptr;
return JsonValue(tag, head);
}
return JsonValue(tag, nullptr);
}
int jsonParse(char *s, char **endptr, JsonValue *value, JsonAllocator &allocator) {
JsonNode *tails[JSON_STACK_SIZE];
JsonTag tags[JSON_STACK_SIZE];
char *keys[JSON_STACK_SIZE];
JsonValue o;
int pos = -1;
bool separator = true;
JsonNode *node;
*endptr = s;
while (*s) {
while (isspace(*s)) {
++s;
if (!*s) break;
}
*endptr = s++;
switch (**endptr) {
case '-':
if (!isdigit(*s) && *s != '.') {
*endptr = s;
return JSON_BAD_NUMBER;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
o = JsonValue(string2double(*endptr, &s));
if (!isdelim(*s)) {
*endptr = s;
return JSON_BAD_NUMBER;
}
break;
case '"':
o = JsonValue(JSON_STRING, s);
for (char *it = s; *s; ++it, ++s) {
int c = *it = *s;
if (c == '\\') {
c = *++s;
switch (c) {
case '\\':
case '"':
case '/':
*it = c;
break;
case 'b':
*it = '\b';
break;
case 'f':
*it = '\f';
break;
case 'n':
*it = '\n';
break;
case 'r':
*it = '\r';
break;
case 't':
*it = '\t';
break;
case 'u':
c = 0;
for (int i = 0; i < 4; ++i) {
if (isxdigit(*++s)) {
c = c * 16 + char2int(*s);
} else {
*endptr = s;
return JSON_BAD_STRING;
}
}
if (c < 0x80) {
*it = c;
} else if (c < 0x800) {
*it++ = 0xC0 | (c >> 6);
*it = 0x80 | (c & 0x3F);
} else {
*it++ = 0xE0 | (c >> 12);
*it++ = 0x80 | ((c >> 6) & 0x3F);
*it = 0x80 | (c & 0x3F);
}
break;
default:
*endptr = s;
return JSON_BAD_STRING;
}
} else if ((unsigned int)c < ' ' || c == '\x7F') {
*endptr = s;
return JSON_BAD_STRING;
} else if (c == '"') {
*it = 0;
++s;
break;
}
}
if (!isdelim(*s)) {
*endptr = s;
return JSON_BAD_STRING;
}
break;
case 't':
if (!(s[0] == 'r' && s[1] == 'u' && s[2] == 'e' && isdelim(s[3])))
return JSON_BAD_IDENTIFIER;
o = JsonValue(JSON_TRUE);
s += 3;
break;
case 'f':
if (!(s[0] == 'a' && s[1] == 'l' && s[2] == 's' && s[3] == 'e' && isdelim(s[4])))
return JSON_BAD_IDENTIFIER;
o = JsonValue(JSON_FALSE);
s += 4;
break;
case 'n':
if (!(s[0] == 'u' && s[1] == 'l' && s[2] == 'l' && isdelim(s[3])))
return JSON_BAD_IDENTIFIER;
o = JsonValue(JSON_NULL);
s += 3;
break;
case ']':
if (pos == -1)
return JSON_STACK_UNDERFLOW;
if (tags[pos] != JSON_ARRAY)
return JSON_MISMATCH_BRACKET;
o = listToValue(JSON_ARRAY, tails[pos--]);
break;
case '}':
if (pos == -1)
return JSON_STACK_UNDERFLOW;
if (tags[pos] != JSON_OBJECT)
return JSON_MISMATCH_BRACKET;
if (keys[pos] != nullptr)
return JSON_UNEXPECTED_CHARACTER;
o = listToValue(JSON_OBJECT, tails[pos--]);
break;
case '[':
if (++pos == JSON_STACK_SIZE)
return JSON_STACK_OVERFLOW;
tails[pos] = nullptr;
tags[pos] = JSON_ARRAY;
keys[pos] = nullptr;
separator = true;
continue;
case '{':
if (++pos == JSON_STACK_SIZE)
return JSON_STACK_OVERFLOW;
tails[pos] = nullptr;
tags[pos] = JSON_OBJECT;
keys[pos] = nullptr;
separator = true;
continue;
case ':':
if (separator || keys[pos] == nullptr)
return JSON_UNEXPECTED_CHARACTER;
separator = true;
continue;
case ',':
if (separator || keys[pos] != nullptr)
return JSON_UNEXPECTED_CHARACTER;
separator = true;
continue;
case '\0':
continue;
default:
return JSON_UNEXPECTED_CHARACTER;
}
separator = false;
if (pos == -1) {
*endptr = s;
*value = o;
return JSON_OK;
}
if (tags[pos] == JSON_OBJECT) {
if (!keys[pos]) {
if (o.getTag() != JSON_STRING)
return JSON_UNQUOTED_KEY;
keys[pos] = o.toString();
continue;
}
if ((node = (JsonNode *) allocator.allocate(sizeof(JsonNode))) == nullptr)
return JSON_ALLOCATION_FAILURE;
tails[pos] = insertAfter(tails[pos], node);
tails[pos]->key = keys[pos];
keys[pos] = nullptr;
} else {
if ((node = (JsonNode *) allocator.allocate(sizeof(JsonNode) - sizeof(char *))) == nullptr)
return JSON_ALLOCATION_FAILURE;
tails[pos] = insertAfter(tails[pos], node);
}
tails[pos]->value = o;
}
return JSON_BREAKING_BAD;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment