Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into restapi
Browse files Browse the repository at this point in the history
  • Loading branch information
nmanovic committed Jan 19, 2019
2 parents 18b6ffb + 4b36f14 commit 7af7452
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 40 deletions.
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ RUN apt-get update && \
apt-get install -y git-lfs && \
git lfs install && \
rm -rf /var/lib/apt/lists/* && \
if [ -n ${socks_proxy} ]; then \
if [ -z ${socks_proxy} ]; then \
echo export "GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30\"" >> ${HOME}/.bashrc; \
else \
echo export "GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30 -o ProxyCommand='nc -X 5 -x ${socks_proxy} %h %p'\"" >> ${HOME}/.bashrc; \
fi

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ CVAT is completely re-designed and re-implemented version of [Video Annotation T

## Screencasts

- [Introduction](https://youtu.be/L9_IvUIHGwM)
- [Annotation mode](https://youtu.be/6h7HxGL6Ct4)
- [Interpolation mode](https://youtu.be/U3MYDhESHo4)
- [Attribute mode](https://youtu.be/UPNfWl8Egd8)
- [Segmentation mode](https://youtu.be/6IJ0QN7PBKo)
- [Segmentation mode](https://youtu.be/Fh8oKuSUIPs)
- [Tutorial for polygons](https://www.youtube.com/watch?v=XTwfXDh4clI)

## LICENSE
Expand Down
14 changes: 10 additions & 4 deletions cvat/apps/engine/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1505,11 +1505,17 @@ def init_from_db(self):
self.points_paths = annotation.points_paths

@plugin_decorator
@transaction.atomic
def _dump(tid, data_format, scheme, host, plugin_meta_data):
db_task = models.Task.objects.select_for_update().get(id=tid)
annotation = _AnnotationForTask(db_task)
annotation.init_from_db()
# For big tasks dump function may run for a long time and
# we dont need to acquire lock after _AnnotationForTask instance
# has been initialized from DB.
# But there is the bug with corrupted dump file in case 2 or more dump request received at the same time.
# https://github.com/opencv/cvat/issues/217
with transaction.atomic():
db_task = models.Task.objects.select_for_update().get(id=tid)
annotation = _AnnotationForTask(db_task)
annotation.init_from_db()

annotation.dump(data_format, scheme, host, plugin_meta_data)

def _calc_box_area(box):
Expand Down
2 changes: 2 additions & 0 deletions cvat/apps/engine/static/engine/js/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ var Logger = {
sendException: 22,
// dumped as "Change frame". There are no additional required fields.
changeFrame: 23,
debugInfo: 24,
},

/**
Expand Down Expand Up @@ -515,6 +516,7 @@ var Logger = {
case this.EventType.sendUserActivity: return 'Send user activity';
case this.EventType.sendException: return 'Send exception';
case this.EventType.changeFrame: return 'Change frame';
case this.EventType.debugInfo: return 'Debug info';
default: return 'Unknown';
}
},
Expand Down
73 changes: 47 additions & 26 deletions cvat/apps/engine/static/engine/js/shapeFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class FilterModel {
constructor(update) {
this._filter = '';
this._filter = "";
this._update = update;
this._labels = window.cvat.labelsInfo.labels();
this._attributes = window.cvat.labelsInfo.attributes();
Expand All @@ -19,8 +19,8 @@ class FilterModel {
return {
id: shape.model.id,
label: shape.model.label,
type: shape.model.type.split('_')[1],
mode: shape.model.type.split('_')[0],
type: shape.model.type.split("_")[1],
mode: shape.model.type.split("_")[0],
occluded: shape.interpolation.position.occluded ? true : false,
attr: convertAttributes(shape.interpolation.attributes),
lock: shape.model.lock
Expand All @@ -30,7 +30,7 @@ class FilterModel {
function convertAttributes(attributes) {
let converted = {};
for (let attrId in attributes) {
converted[attributes[attrId].name.toLowerCase().replace(/-/g, "_")] = ('' + attributes[attrId].value).toLowerCase();
converted[attributes[attrId].name.toLowerCase().replace(/-/g, "_")] = ("" + attributes[attrId].value).toLowerCase();
}
return converted;
}
Expand Down Expand Up @@ -79,9 +79,9 @@ class FilterController {

updateFilter(value, silent) {
if (value.length) {
value = value.split('|').map(x => '/d:data/' + x).join('|').toLowerCase().replace(/-/g, "_");
value = value.split("|").map(x => "/d:data/" + x).join("|").toLowerCase().replace(/-/g, "_");
try {
document.evaluate(value, document, () => 'ns');
document.evaluate(value, document, () => "ns");
}
catch (error) {
return false;
Expand All @@ -90,7 +90,7 @@ class FilterController {
return true;
}
else {
this._model.updateFilter('', silent);
this._model.updateFilter("", silent);
return true;
}
}
Expand All @@ -104,41 +104,62 @@ class FilterController {
class FilterView {
constructor(filterController) {
this._controller = filterController;
this._filterString = $('#filterInputString');
this._resetFilterButton = $('#resetFilterButton');
this._filterString = $("#filterInputString");
this._resetFilterButton = $("#resetFilterButton");
this._filterString.on("keypress keydown keyup", (e) => e.stopPropagation());
this._filterSubmitList = $("#filterSubmitList");

let predefinedValues = null;
try {
predefinedValues = JSON.parse(localStorage.getItem("filterValues")) || [];
}
catch {
predefinedValues = [];
}

let initSubmitList = () => {
this._filterSubmitList.empty();
for (let value of predefinedValues) {
this._filterSubmitList.append(`<option value=${value}> ${value} </option>`);
}
}
initSubmitList();

this._filterString.attr('placeholder', 'car[attr/model="mazda"]');
this._filterString.on('keypress keydown keyup', (e) => e.stopPropagation());
this._filterString.on('change', (e) => {
this._filterString.on("change", (e) => {
let value = $.trim(e.target.value);
if (this._controller.updateFilter(value, false)) {
this._filterString.css('color', 'green');
this._filterString.css("color", "green");
if (!predefinedValues.includes(value)) {
predefinedValues = (predefinedValues.concat([value])).slice(-10);
localStorage.setItem("filterValues", JSON.stringify(predefinedValues));
initSubmitList();
}
}
else {
this._filterString.css('color', 'red');
this._controller.updateFilter('', false);
this._filterString.css("color", "red");
this._controller.updateFilter("", false);
}
});

let shortkeys = window.cvat.config.shortkeys;
this._filterString.attr('title', `
${shortkeys['prev_filter_frame'].view_value} - ${shortkeys['prev_filter_frame'].description}` + `\n` +
`${shortkeys['next_filter_frame'].view_value} - ${shortkeys['next_filter_frame'].description}`);
this._filterString.attr("title", `
${shortkeys["prev_filter_frame"].view_value} - ${shortkeys["prev_filter_frame"].description}` + `\n` +
`${shortkeys["next_filter_frame"].view_value} - ${shortkeys["next_filter_frame"].description}`);

this._resetFilterButton.on('click', () => {
this._filterString.prop('value', '');
this._controller.updateFilter('', false);
this._resetFilterButton.on("click", () => {
this._filterString.prop("value", "");
this._controller.updateFilter("", false);
});

let initialFilter = window.cvat.search.get('filter');
let initialFilter = window.cvat.search.get("filter");
if (initialFilter) {
this._filterString.prop('value', initialFilter);
this._filterString.prop("value", initialFilter);
if (this._controller.updateFilter(initialFilter, true)) {
this._filterString.css('color', 'green');
this._filterString.css("color", "green");
}
else {
this._filterString.prop('value', '');
this._filterString.css('color', 'red');
this._filterString.prop("value", "");
this._filterString.css("color", "red");
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions cvat/apps/engine/static/engine/js/shapes.js
Original file line number Diff line number Diff line change
Expand Up @@ -1475,9 +1475,19 @@ class ShapeView extends Listener {
blurAllElements();
this._hideShapeText();
this.notify('resize');

Logger.addEvent(Logger.EventType.debugInfo, {
debugMessage: "Resize has started",
resizeEventInitialized: Boolean(events.resize)
});
}).on('resizing', () => {
objWasResized = true;
}).on('resizedone', () => {
Logger.addEvent(Logger.EventType.debugInfo, {
debugMessage: "Resize has done",
resizeEventInitialized: Boolean(events.resize)
});

if (objWasResized) {
let frame = window.cvat.player.frames.current;
this._controller.updatePosition(frame, this._buildPosition());
Expand Down
3 changes: 2 additions & 1 deletion cvat/apps/engine/templates/engine/annotation.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@
<div style="margin-top: 20px">
<button id="menuButton" class="regular h1"> Open Menu </button>
<label class="regular h1" style="margin-left: 50px"> Filter: </label>
<input type="text" id="filterInputString" class="regular h2"/>
<datalist id="filterSubmitList" style="display: none;"> </datalist>
<input type="text" list="filterSubmitList" id="filterInputString" class="regular h2" placeholder='car[attr/model=/"mazda"'/>
<button id="resetFilterButton" class="regular h1"> Reset </button>
<button class="regular h1" id="undoButton" disabled> &#x27F2; </button>
<select size="2" class="regular" style="overflow: hidden; width: 15%; top: 0.5em; position: relative;" disabled>
Expand Down
32 changes: 25 additions & 7 deletions utils/coco/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ def draw_polygons(polygons, img_name, input_dir, output_dir, draw_labels):
red = (0, 0, 255)
for poly in polygons:
label = poly['label']
rle = mask_util.frPyObjects(poly['points'], img.shape[0], img.shape[1])
bbox = mask_util.toBbox(rle)
_, bbox = polygon_area_and_bbox(poly['points'], img.shape[0], img.shape[1])
for j in range(0, len(poly['points'])):
i = 0
points = []
Expand All @@ -106,10 +105,12 @@ def draw_polygons(polygons, img_name, input_dir, output_dir, draw_labels):
y = int(poly['points'][j][i + 1])
points.append([x, y])
i += 2
bbox = [int(value) for value in bbox]
img = cv2.polylines(img, np.int32([points]), True, yellow, 1)
img = cv2.rectangle(img, (bbox[0], bbox[1]), (bbox[0] + bbox[2], bbox[1] + bbox[3]), red, 2)
if draw_labels:
x = int(bbox[0][0]) + int(bbox[0][2] / 4)
y = int(bbox[0][1]) + int(bbox[0][3] / 2)
x = bbox[0] + bbox[2] // 4
y = bbox[1] + bbox[3] // 2
cv2.putText(img, label, (x, y), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, red, 1)
cv2.imwrite(output_file, img)

Expand Down Expand Up @@ -206,6 +207,10 @@ def polygon_area_and_bbox(polygon, height, width):
rle = mask_util.frPyObjects(polygon, height, width)
area = mask_util.area(rle)
bbox = mask_util.toBbox(rle)
bbox = [min(bbox[:, 0]),
min(bbox[:, 1]),
max(bbox[:, 0] + bbox[:, 2]) - min(bbox[:, 0]),
max(bbox[:, 1] + bbox[:, 3]) - min(bbox[:, 1])]
return area, bbox

def insert_license_data(result_annotation):
Expand Down Expand Up @@ -337,8 +342,8 @@ def insert_annotation_data(image, category_map, segm_id, object, img_dims, resul
new_anno['iscrowd'] = 0
new_anno['segmentation'] = object['points']
area, bbox = polygon_area_and_bbox(object['points'], img_dims[0], img_dims[1])
new_anno['area'] = float(area[0])
new_anno['bbox'] = [bbox[0][0], bbox[0][1], bbox[0][2], bbox[0][3]]
new_anno['area'] = float(np.sum(area))
new_anno['bbox'] = bbox
result_annotation['annotations'].append(new_anno)


Expand Down Expand Up @@ -368,18 +373,28 @@ def main():
'annotation directory includes file <labels.txt>'.format(xml_file_name))

segm_id = 0
z_order_off_counter = 0
# Parse original annotation
for img in tqdm(root.iter('image'), desc='Processing images from ' + xml_file_name):
image = {}
for key, value in img.items():
image[key] = value
image['polygon'] = []
z_order_on_counter = 0
polygon_counter = 0
for poly in img.iter('polygon'):
polygon = {}
for key, value in poly.items():
polygon[key] = value
if key == 'z_order':
z_order_on_counter += 1
polygon_counter += 1
image['polygon'].append(polygon)
image['polygon'].sort(key=lambda x: int(x['z_order']))
# If at least one of polygons on image does not have field 'z_order' do not sort them
if z_order_on_counter == polygon_counter:
image['polygon'].sort(key=lambda x: int(x['z_order']))
else:
z_order_off_counter += 1

# Create new image
insert_image_data(image, args.image_dir, result_annotation)
Expand All @@ -399,6 +414,9 @@ def main():

log.info('Processed images: {}'.format(len(result_annotation['images'])))
log.info('Processed objects: {}'.format(len(result_annotation['annotations'])))
if z_order_off_counter > 0:
log.warning('Annotation does not have a field \'z_order\' for {} image(s). '
'Overlapped objects may be cropped incorrectly!'. format(z_order_off_counter))

# Save created annotation
log.info('Saving annotation...')
Expand Down

0 comments on commit 7af7452

Please sign in to comment.