diff --git a/CHANGELOG.md b/CHANGELOG.md index 858ec855..fc541fce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.1.0-alpha] - Unreleased ### Added - Throttling policy for unauthenticated users () +- Added default label color table for mask export (https://github.com/opencv/cvat/pull/1549) ### Changed - Removed information about e-mail from the basic user information () diff --git a/cvat/apps/dataset_manager/formats/mask.py b/cvat/apps/dataset_manager/formats/mask.py index 492fed38..56693532 100644 --- a/cvat/apps/dataset_manager/formats/mask.py +++ b/cvat/apps/dataset_manager/formats/mask.py @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: MIT +import os.path as osp from tempfile import TemporaryDirectory from pyunpack import Archive @@ -9,7 +10,9 @@ from pyunpack import Archive from cvat.apps.dataset_manager.bindings import (CvatTaskDataExtractor, import_dm_annotations) from cvat.apps.dataset_manager.util import make_zip_archive +from datumaro.cli.util import make_file_name from datumaro.components.project import Dataset +from datumaro.util.mask_tools import generate_colormap from .registry import dm_env, exporter, importer @@ -25,7 +28,8 @@ def _export(dst_file, task_data, save_images=False): extractor = Dataset.from_extractors(extractor) # apply lazy transforms with TemporaryDirectory() as temp_dir: converter = dm_env.make_converter('voc_segmentation', - apply_colormap=True, label_map='source', save_images=save_images) + apply_colormap=True, label_map=make_colormap(task_data), + save_images=save_images) converter(extractor, save_dir=temp_dir) make_zip_archive(temp_dir, dst_file) @@ -39,3 +43,43 @@ def _import(src_file, task_data): masks_to_polygons = dm_env.transforms.get('masks_to_polygons') dataset = dataset.transform(masks_to_polygons) import_dm_annotations(dataset, task_data) + + +DEFAULT_COLORMAP_CAPACITY = 2000 +DEFAULT_COLORMAP_PATH = osp.join(osp.dirname(__file__), 'predefined_colors.txt') +def parse_default_colors(file_path=None): + if file_path is None: + file_path = DEFAULT_COLORMAP_PATH + + colors = {} + with open(file_path) as f: + for line in f: + line = line.strip() + if not line or line[0] == '#': + continue + _, label, color = line.split(':') + colors[label] = tuple(map(int, color.split(','))) + return colors + +def normalize_label(label): + label = make_file_name(label) # basically, convert to ASCII lowercase + label = label.replace('-', '_') + return label + +def make_colormap(task_data): + labels = sorted([label['name'] + for _, label in task_data.meta['task']['labels']]) + if 'background' not in labels: + labels.insert(0, 'background') + + predefined = parse_default_colors() + + # NOTE: using pop() to avoid collisions + colormap = {k: predefined.pop(normalize_label(k), None) for k in labels} + + random_labels = [k for k in labels if not colormap[k]] + if random_labels: + colors = generate_colormap(DEFAULT_COLORMAP_CAPACITY + len(random_labels)) + for i, label in enumerate(random_labels): + colormap[label] = colors[DEFAULT_COLORMAP_CAPACITY + i] + return {l: [c, [], []] for l, c in colormap.items()} \ No newline at end of file diff --git a/cvat/apps/dataset_manager/formats/predefined_colors.txt b/cvat/apps/dataset_manager/formats/predefined_colors.txt new file mode 100644 index 00000000..4aee0d78 --- /dev/null +++ b/cvat/apps/dataset_manager/formats/predefined_colors.txt @@ -0,0 +1,731 @@ +# ImageNet + OpenImages + PASCAL VOC + +# CamVid + CityScapes + Kitti + +# COCO + COCO stuff + +# Format: +# pascal idx : normalized label name : R,G,B + +0:background:0,0,0 +1:accordion:128,0,0 +2:adhesive_tape:0,128,0 +3:aeroplane:128,128,0 +4:aircraft:0,0,128 +5:airplane:128,0,128 +6:alarm_clock:0,128,128 +7:alpaca:128,128,128 +8:ambulance:64,0,0 +9:animal:192,0,0 +10:ant:64,128,0 +11:antelope:192,128,0 +12:apple:64,0,128 +13:armadillo:192,0,128 +14:artichoke:64,128,128 +15:asparagus:192,128,128 +16:axe:0,64,0 +17:baby_bed:128,64,0 +18:backpack:0,192,0 +19:bagel:128,192,0 +20:balance_beam:0,64,128 +21:ball:128,64,128 +22:balloon:0,192,128 +23:banana:128,192,128 +24:band_aid:64,64,0 +25:banjo:192,64,0 +26:banner:64,192,0 +27:barge:192,192,0 +28:barrel:64,64,128 +29:baseball:192,64,128 +30:baseball_bat:64,192,128 +31:baseball_glove:192,192,128 +32:basketball:0,0,64 +33:bat:128,0,64 +34:bathing_cap:0,128,64 +35:bathroom_cabinet:128,128,64 +36:bathtub:0,0,192 +37:beaker:128,0,192 +38:bear:0,128,192 +39:bed:128,128,192 +40:bee:64,0,64 +41:beehive:192,0,64 +42:beer:64,128,64 +43:beetle:192,128,64 +44:bell_pepper:64,0,192 +45:belt:192,0,192 +46:bench:64,128,192 +47:bg:192,128,192 +48:bicycle:0,64,64 +49:bicycle_helmet:128,64,64 +50:bicycle_wheel:0,192,64 +51:bidet:128,192,64 +52:billboard:0,64,192 +53:billiard_table:128,64,192 +54:binder:0,192,192 +55:binoculars:128,192,192 +56:bird:64,64,64 +57:blanket:192,64,64 +58:blender:64,192,64 +59:blue_jay:192,192,64 +60:boat:64,64,192 +61:book:192,64,192 +62:bookcase:64,192,192 +63:bookshelf:192,192,192 +64:boot:32,0,0 +65:bottle:160,0,0 +66:bow:32,128,0 +67:bow_and_arrow:160,128,0 +68:bow_tie:32,0,128 +69:bowl:160,0,128 +70:box:32,128,128 +71:boy:160,128,128 +72:branch:96,0,0 +73:brassiere:224,0,0 +74:bread:96,128,0 +75:bridge:224,128,0 +76:briefcase:96,0,128 +77:broccoli:224,0,128 +78:bronze_sculpture:96,128,128 +79:brown_bear:224,128,128 +80:building:32,64,0 +81:building_other:160,64,0 +82:bull:32,192,0 +83:burrito:160,192,0 +84:bus:32,64,128 +85:bush:160,64,128 +86:bust:32,192,128 +87:butterfly:160,192,128 +88:cabbage:96,64,0 +89:cabinet:224,64,0 +90:cabinetry:96,192,0 +91:cage:224,192,0 +92:cake:96,64,128 +93:cake_stand:224,64,128 +94:camel:96,192,128 +95:camera:224,192,128 +96:can_opener:32,0,64 +97:canary:160,0,64 +98:candle:32,128,64 +99:candy:160,128,64 +100:cannon:32,0,192 +101:canoe:160,0,192 +102:car:32,128,192 +103:caravan:160,128,192 +104:cardboard:96,0,64 +105:carnivore:224,0,64 +106:carpet:96,128,64 +107:carrot:224,128,64 +108:cart:96,0,192 +109:castle:224,0,192 +110:cat:96,128,192 +111:caterpillar:224,128,192 +112:cattle:32,64,64 +113:ceiling_fan:160,64,64 +114:ceiling_other:32,192,64 +115:ceiling_tile:160,192,64 +116:cell_phone:32,64,192 +117:cello:160,64,192 +118:centipede:32,192,192 +119:chain_saw:160,192,192 +120:chair:96,64,64 +121:cheetah:224,64,64 +122:chest_of_drawers:96,192,64 +123:chicken:224,192,64 +124:chime:96,64,192 +125:chopsticks:224,64,192 +126:christmas_tree:96,192,192 +127:clock:224,192,192 +128:cloth:0,32,0 +129:clothes:128,32,0 +130:clouds:0,160,0 +131:coat:128,160,0 +132:cocktail:0,32,128 +133:cocktail_shaker:128,32,128 +134:coconut:0,160,128 +135:coffee:128,160,128 +136:coffee_cup:64,32,0 +137:coffee_maker:192,32,0 +138:coffee_table:64,160,0 +139:coffeemaker:192,160,0 +140:coin:64,32,128 +141:common_fig:192,32,128 +142:computer_keyboard:64,160,128 +143:computer_monitor:192,160,128 +144:computer_mouse:0,96,0 +145:construction:128,96,0 +146:convenience_store:0,224,0 +147:cookie:128,224,0 +148:corded_phone:0,96,128 +149:corkscrew:128,96,128 +150:couch:0,224,128 +151:counter:128,224,128 +152:countertop:64,96,0 +153:cow:192,96,0 +154:cowboy_hat:64,224,0 +155:crab:192,224,0 +156:cream:64,96,128 +157:cricket_ball:192,96,128 +158:crocodile:64,224,128 +159:croissant:192,224,128 +160:croquet_ball:0,32,64 +161:crosswalk:128,32,64 +162:crown:0,160,64 +163:crutch:128,160,64 +164:cucumber:0,32,192 +165:cup:128,32,192 +166:cup_or_mug:0,160,192 +167:cupboard:128,160,192 +168:curtain:64,32,64 +169:cutting_board:192,32,64 +170:cyclist:64,160,64 +171:dagger:192,160,64 +172:deer:64,32,192 +173:desk:192,32,192 +174:desk_stuff:64,160,192 +175:dessert:192,160,192 +176:diaper:0,96,64 +177:dice:128,96,64 +178:digital_clock:0,224,64 +179:dining_table:128,224,64 +180:diningtable:0,96,192 +181:dinosaur:128,96,192 +182:dirt:0,224,192 +183:dishwasher:128,224,192 +184:dog:64,96,64 +185:dog_bed:192,96,64 +186:doll:64,224,64 +187:dolphin:192,224,64 +188:domestic_cat:64,96,192 +189:dontcare:192,96,192 +190:donut:64,224,192 +191:door:192,224,192 +192:door_handle:32,32,0 +193:door_stuff:160,32,0 +194:doughnut:32,160,0 +195:dragonfly:160,160,0 +196:drawer:32,32,128 +197:dress:160,32,128 +198:drink:32,160,128 +199:drinking_straw:160,160,128 +200:drum:96,32,0 +201:duck:224,32,0 +202:dumbbell:96,160,0 +203:dynamic:224,160,0 +204:eagle:96,32,128 +205:earrings:224,32,128 +206:egg:96,160,128 +207:electric_fan:224,160,128 +208:elephant:32,96,0 +209:envelope:160,96,0 +210:eye_glasses:32,224,0 +211:face_powder:160,224,0 +212:falcon:32,96,128 +213:fedora:160,96,128 +214:fence:32,224,128 +215:fence_guard:160,224,128 +216:fig:96,96,0 +217:filing_cabinet:224,96,0 +218:finish:96,224,0 +219:fire_hydrant:224,224,0 +220:fireplace:96,96,128 +221:fish:224,96,128 +222:flag:96,224,128 +223:flashlight:224,224,128 +224:floor_marble:32,32,64 +225:floor_other:160,32,64 +226:floor_stone:32,160,64 +227:floor_tile:160,160,64 +228:floor_wood:32,32,192 +229:flower:160,32,192 +230:flower_pot:32,160,192 +231:flowerpot:160,160,192 +232:flute:96,32,64 +233:fog:224,32,64 +234:food_other:96,160,64 +235:food_processor:224,160,64 +236:football:96,32,192 +237:football_helmet:224,32,192 +238:footwear:96,160,192 +239:fork:224,160,192 +240:fountain:32,96,64 +241:fox:160,96,64 +242:french_fries:32,224,64 +243:french_horn:160,224,64 +244:frisbee:32,96,192 +245:frog:160,96,192 +246:fruit:32,224,192 +247:frying_pan:160,224,192 +248:furniture:96,96,64 +249:furniture_other:224,96,64 +250:gas_stove:96,224,64 +251:giant_panda:224,224,64 +252:giraffe:96,96,192 +253:girl:224,96,192 +254:glasses:96,224,192 +255:glove:224,224,192 +256:goat:0,0,32 +257:goggles:128,0,32 +258:goldfish:0,128,32 +259:golf_ball:128,128,32 +260:golf_cart:0,0,160 +261:golfcart:128,0,160 +262:gondola:0,128,160 +263:goose:128,128,160 +264:grape:64,0,32 +265:grapefruit:192,0,32 +266:grass:64,128,32 +267:gravel:192,128,32 +268:ground:64,0,160 +269:ground_other:192,0,160 +270:guacamole:64,128,160 +271:guitar:192,128,160 +272:hair_brush:0,64,32 +273:hair_drier:128,64,32 +274:hair_dryer:0,192,32 +275:hair_spray:128,192,32 +276:hamburger:0,64,160 +277:hammer:128,64,160 +278:hamster:0,192,160 +279:handbag:128,192,160 +280:handgun:64,64,32 +281:harbor_seal:192,64,32 +282:harmonica:64,192,32 +283:harp:192,192,32 +284:harpsichord:64,64,160 +285:hat:192,64,160 +286:hat_with_a_wide_brim:64,192,160 +287:head_cabbage:192,192,160 +288:headphones:0,0,96 +289:helicopter:128,0,96 +290:helmet:0,128,96 +291:high_heels:128,128,96 +292:hill:0,0,224 +293:hippopotamus:128,0,224 +294:home_appliance:0,128,224 +295:honeycomb:128,128,224 +296:horizontal_bar:64,0,96 +297:horn:192,0,96 +298:horse:64,128,96 +299:hot_dog:192,128,96 +300:hotdog:64,0,224 +301:house:192,0,224 +302:houseplant:64,128,224 +303:human:192,128,224 +304:human_arm:0,64,96 +305:human_beard:128,64,96 +306:human_ear:0,192,96 +307:human_eye:128,192,96 +308:human_face:0,64,224 +309:human_foot:128,64,224 +310:human_hair:0,192,224 +311:human_hand:128,192,224 +312:human_head:64,64,96 +313:human_leg:192,64,96 +314:human_mouth:64,192,96 +315:human_nose:192,192,96 +316:ice_cream:64,64,224 +317:ignore:192,64,224 +318:infant_bed:64,192,224 +319:insect:192,192,224 +320:invertebrate:32,0,32 +321:ipod:160,0,32 +322:isopod:32,128,32 +323:jacket:160,128,32 +324:jaguar:32,0,160 +325:jeans:160,0,160 +326:jellyfish:32,128,160 +327:jet_ski:160,128,160 +328:jug:96,0,32 +329:juice:224,0,32 +330:kangaroo:96,128,32 +331:kettle:224,128,32 +332:keyboard:96,0,160 +333:kitchen_appliance:224,0,160 +334:kitchen_dining_room_table:96,128,160 +335:kitchen_knife:224,128,160 +336:kite:32,64,32 +337:knife:160,64,32 +338:koala_bear:32,192,32 +339:ladder:160,192,32 +340:ladle:32,64,160 +341:ladybug:160,64,160 +342:lamp:32,192,160 +343:land_vehicle:160,192,160 +344:lantern:96,64,32 +345:laptop:224,64,32 +346:lavender:96,192,32 +347:leaves:224,192,32 +348:lemon:96,64,160 +349:leopard:224,64,160 +350:license_plate:96,192,160 +351:lifejacket:224,192,160 +352:light:32,0,96 +353:light_bulb:160,0,96 +354:light_switch:32,128,96 +355:lighthouse:160,128,96 +356:lily:32,0,224 +357:limousine:160,0,224 +358:lion:32,128,224 +359:lipstick:160,128,224 +360:lizard:96,0,96 +361:lobster:224,0,96 +362:loveseat:96,128,96 +363:luggage_and_bags:224,128,96 +364:lynx:96,0,224 +365:maillot:224,0,224 +366:man:96,128,224 +367:mango:224,128,224 +368:maple:32,64,96 +369:maraca:160,64,96 +370:marine_invertebrates:32,192,96 +371:marine_mammal:160,192,96 +372:mat:32,64,224 +373:measuring_cup:160,64,224 +374:mechanical_fan:32,192,224 +375:metal:160,192,224 +376:microphone:96,64,96 +377:microwave:224,64,96 +378:microwave_oven:96,192,96 +379:milk_can:224,192,96 +380:miniskirt:96,64,224 +381:mirror:224,64,224 +382:mirror_stuff:96,192,224 +383:misc:224,192,224 +384:missile:0,32,32 +385:mixer:128,32,32 +386:mobile_phone:0,160,32 +387:monkey:128,160,32 +388:moss:0,32,160 +389:moths_and_butterflies:128,32,160 +390:motorbike:0,160,160 +391:motorcycle:128,160,160 +392:mountain:64,32,32 +393:mouse:192,32,32 +394:mud:64,160,32 +395:muffin:192,160,32 +396:mug:64,32,160 +397:mule:192,32,160 +398:mushroom:64,160,160 +399:musical_instrument:192,160,160 +400:musical_keyboard:0,96,32 +401:nail:128,96,32 +402:napkin:0,224,32 +403:nature:128,224,32 +404:neck_brace:0,96,160 +405:necklace:128,96,160 +406:net:0,224,160 +407:nightstand:128,224,160 +408:non_vehicle:64,96,32 +409:obj:192,96,32 +410:object:64,224,32 +411:oboe:192,224,32 +412:office_building:64,96,160 +413:office_supplies:192,96,160 +414:on_rails:64,224,160 +415:orange:192,224,160 +416:organ:0,32,96 +417:ostrich:128,32,96 +418:otter:0,160,96 +419:oven:128,160,96 +420:owl:0,32,224 +421:oyster:128,32,224 +422:paddle:0,160,224 +423:palm_tree:128,160,224 +424:pancake:64,32,96 +425:paper:192,32,96 +426:paper_towel:64,160,96 +427:parachute:192,160,96 +428:parking:64,32,224 +429:parking_meter:192,32,224 +430:parrot:64,160,224 +431:pasta:192,160,224 +432:pavement:0,96,96 +433:peach:128,96,96 +434:pear:0,224,96 +435:pedestrian:128,224,96 +436:pen:0,96,224 +437:pencil_box:128,96,224 +438:pencil_sharpener:0,224,224 +439:penguin:128,224,224 +440:perfume:64,96,96 +441:person:192,96,96 +442:personal_care:64,224,96 +443:piano:192,224,96 +444:picnic_basket:64,96,224 +445:picture_frame:192,96,224 +446:pig:64,224,224 +447:pillow:192,224,224 +448:pineapple:32,32,32 +449:ping_pong_ball:160,32,32 +450:pitcher:32,160,32 +451:pizza:160,160,32 +452:plant_other:32,32,160 +453:plastic:160,32,160 +454:plastic_bag:32,160,160 +455:plate:160,160,160 +456:plate_rack:96,32,32 +457:platform:224,32,32 +458:platter:96,160,32 +459:playingfield:224,160,32 +460:plumbing_fixture:96,32,160 +461:polar_bear:224,32,160 +462:pole:96,160,160 +463:pole_group:224,160,160 +464:pomegranate:32,96,32 +465:popcorn:160,96,32 +466:popsicle:32,224,32 +467:porch:160,224,32 +468:porcupine:32,96,160 +469:poster:160,96,160 +470:potato:32,224,160 +471:potted_plant:160,224,160 +472:pottedplant:96,96,32 +473:power_drill:224,96,32 +474:power_plugs_and_sockets:96,224,32 +475:pressure_cooker:224,224,32 +476:pretzel:96,96,160 +477:printer:224,96,160 +478:puck:96,224,160 +479:pumpkin:224,224,160 +480:punching_bag:32,32,96 +481:purse:160,32,96 +482:rabbit:32,160,96 +483:raccoon:160,160,96 +484:racket:32,32,224 +485:radish:160,32,224 +486:rail:32,160,224 +487:rail_track:160,160,224 +488:railing:96,32,96 +489:railroad:224,32,96 +490:raven:96,160,96 +491:ray:224,160,96 +492:red_panda:96,32,224 +493:refrigerator:224,32,224 +494:remote:96,160,224 +495:remote_control:224,160,224 +496:reptile:32,96,96 +497:rhinoceros:160,96,96 +498:rider:32,224,96 +499:rifle:160,224,96 +500:ring_binder:32,96,224 +501:river:160,96,224 +502:road:32,224,224 +503:rock:160,224,224 +504:rocket:96,96,96 +505:roller_skates:224,96,96 +506:roof:96,224,96 +507:rose:224,224,96 +508:rubber_eraser:96,96,224 +509:rug:224,96,224 +510:rugby_ball:96,224,224 +511:ruler:224,224,224 +512:salad:16,0,0 +513:salt_and_pepper_shakers:144,0,0 +514:salt_or_pepper_shaker:16,128,0 +515:sand:144,128,0 +516:sandal:16,0,128 +517:sandwich:144,0,128 +518:saucer:16,128,128 +519:saxophone:144,128,128 +520:scarf:80,0,0 +521:scissors:208,0,0 +522:scoreboard:80,128,0 +523:scorpion:208,128,0 +524:screwdriver:80,0,128 +525:sculpture:208,0,128 +526:sea:80,128,128 +527:sea_lion:208,128,128 +528:sea_turtle:16,64,0 +529:seafood:144,64,0 +530:seahorse:16,192,0 +531:seal:144,192,0 +532:seat_belt:16,64,128 +533:segway:144,64,128 +534:serving_tray:16,192,128 +535:sewing_machine:144,192,128 +536:shark:80,64,0 +537:sheep:208,64,0 +538:shelf:80,192,0 +539:shellfish:208,192,0 +540:shirt:80,64,128 +541:shoe:208,64,128 +542:shorts:80,192,128 +543:shotgun:208,192,128 +544:shower:16,0,64 +545:shrimp:144,0,64 +546:sidewalk:16,128,64 +547:sink:144,128,64 +548:skateboard:16,0,192 +549:ski:144,0,192 +550:skirt:16,128,192 +551:skis:144,128,192 +552:skull:80,0,64 +553:skunk:208,0,64 +554:sky:80,128,64 +555:sky_other:208,128,64 +556:skyscraper:80,0,192 +557:slow_cooker:208,0,192 +558:snail:80,128,192 +559:snake:208,128,192 +560:snow:16,64,64 +561:snowboard:144,64,64 +562:snowman:16,192,64 +563:snowmobile:144,192,64 +564:snowplow:16,64,192 +565:soap_dispenser:144,64,192 +566:soccer_ball:16,192,192 +567:sock:144,192,192 +568:sofa:80,64,64 +569:sofa_bed:208,64,64 +570:solid_other:80,192,64 +571:sombrero:208,192,64 +572:sparrow:80,64,192 +573:spatula:208,64,192 +574:spider:80,192,192 +575:spoon:208,192,192 +576:sports_ball:48,0,0 +577:sports_uniform:176,0,0 +578:squash:48,128,0 +579:squirrel:176,128,0 +580:stairs:48,0,128 +581:starfish:176,0,128 +582:start:48,128,128 +583:static:176,128,128 +584:stationary_bicycle:112,0,0 +585:stethoscope:240,0,0 +586:stone:112,128,0 +587:stool:240,128,0 +588:stop:112,0,128 +589:stop_sign:240,0,128 +590:stove:112,128,128 +591:strainer:240,128,128 +592:straw:48,64,0 +593:strawberry:176,64,0 +594:street_light:48,192,0 +595:street_sign:176,192,0 +596:stretcher:48,64,128 +597:structural_other:176,64,128 +598:studio_couch:48,192,128 +599:submarine_sandwich:176,192,128 +600:suit:112,64,0 +601:suitcase:240,64,0 +602:sun_hat:112,192,0 +603:sunflower:240,192,0 +604:sunglasses:112,64,128 +605:surfboard:240,64,128 +606:sushi:112,192,128 +607:swan:240,192,128 +608:swim_cap:48,0,64 +609:swimming_pool:176,0,64 +610:swimming_trunks:48,128,64 +611:swimwear:176,128,64 +612:swine:48,0,192 +613:sword:176,0,192 +614:syringe:48,128,192 +615:table:176,128,192 +616:table_tennis_racket:112,0,64 +617:tablet_computer:240,0,64 +618:tableware:112,128,64 +619:taco:240,128,64 +620:tank:112,0,192 +621:tap:240,0,192 +622:tape_player:112,128,192 +623:tart:240,128,192 +624:taxi:48,64,64 +625:tea:176,64,64 +626:teapot:48,192,64 +627:teddy_bear:176,192,64 +628:telephone:48,64,192 +629:television:176,64,192 +630:tennis_ball:48,192,192 +631:tennis_racket:176,192,192 +632:tent:112,64,64 +633:terrain:240,64,64 +634:textile_other:112,192,64 +635:tiara:240,192,64 +636:tick:112,64,192 +637:tie:240,64,192 +638:tiger:112,192,192 +639:tin_can:240,192,192 +640:tire:16,32,0 +641:toaster:144,32,0 +642:toilet:16,160,0 +643:toilet_paper:144,160,0 +644:tomato:16,32,128 +645:toothbrush:144,32,128 +646:torch:16,160,128 +647:tortoise:144,160,128 +648:towel:80,32,0 +649:tower:208,32,0 +650:toy:80,160,0 +651:traffic_light:208,160,0 +652:traffic_sign:80,32,128 +653:trailer:208,32,128 +654:train:80,160,128 +655:tram:208,160,128 +656:treadmill:16,96,0 +657:tree:144,96,0 +658:tripod:16,224,0 +659:trombone:144,224,0 +660:trousers:16,96,128 +661:truck:144,96,128 +662:trumpet:16,224,128 +663:tunnel:144,224,128 +664:turkey:80,96,0 +665:turtle:208,96,0 +666:tv:80,224,0 +667:tv_or_monitor:208,224,0 +668:tvmonitor:80,96,128 +669:umbrella:208,96,128 +670:undefined:80,224,128 +671:unicycle:208,224,128 +672:unlabeled:16,32,64 +673:vacuum:144,32,64 +674:van:16,160,64 +675:vase:144,160,64 +676:vegetable:16,32,192 +677:vegetation:144,32,192 +678:vehicle:16,160,192 +679:vehicle_registration_plate:144,160,192 +680:violin:80,32,64 +681:void:208,32,64 +682:volleyball:80,160,64 +683:waffle:208,160,64 +684:waffle_iron:80,32,192 +685:wall:208,32,192 +686:wall_brick:80,160,192 +687:wall_clock:208,160,192 +688:wall_concrete:16,96,64 +689:wall_other:144,96,64 +690:wall_panel:16,224,64 +691:wall_stone:144,224,64 +692:wall_tile:16,96,192 +693:wall_wood:144,96,192 +694:washer:16,224,192 +695:washing_machine:144,224,192 +696:waste_container:80,96,64 +697:watch:208,96,64 +698:water_bottle:80,224,64 +699:water_other:208,224,64 +700:watercraft:80,96,192 +701:waterdrops:208,96,192 +702:watermelon:80,224,192 +703:weapon:208,224,192 +704:whale:48,32,0 +705:wheel:176,32,0 +706:wheelchair:48,160,0 +707:whiteboard:176,160,0 +708:willow:48,32,128 +709:window:176,32,128 +710:window_blind:48,160,128 +711:window_other:176,160,128 +712:wine:112,32,0 +713:wine_bottle:240,32,0 +714:wine_glass:112,160,0 +715:winter_melon:240,160,0 +716:wok:112,32,128 +717:woman:240,32,128 +718:wood:112,160,128 +719:wood_burning_stove:240,160,128 +720:woodpecker:48,96,0 +721:wrench:176,96,0 +722:zebra:48,224,0 +723:zucchini:176,224,0 diff --git a/datumaro/datumaro/plugins/voc_format/converter.py b/datumaro/datumaro/plugins/voc_format/converter.py index 01394a5d..234b83be 100644 --- a/datumaro/datumaro/plugins/voc_format/converter.py +++ b/datumaro/datumaro/plugins/voc_format/converter.py @@ -110,8 +110,8 @@ class _Converter: self._images_dir = images_dir def get_label(self, label_id): - return self._strip_label(self._extractor. \ - categories()[AnnotationType.label].items[label_id].name) + return self._extractor. \ + categories()[AnnotationType.label].items[label_id].name def save_subsets(self): subsets = self._extractor.subsets() @@ -207,7 +207,7 @@ class _Converter: obj_elem = ET.SubElement(root_elem, 'object') - obj_label = self.get_label(obj.label) + obj_label = self.get_label(obj.label) ET.SubElement(obj_elem, 'name').text = obj_label if 'pose' in attr: @@ -357,8 +357,7 @@ class _Converter: for item, item_labels in class_lists.items(): if not item_labels: continue - item_labels = [self._strip_label(self.get_label(l)) - for l in item_labels] + item_labels = [self.get_label(l) for l in item_labels] presented = label in item_labels f.write('%s % d\n' % (item, 1 if presented else -1)) @@ -410,23 +409,37 @@ class _Converter: path = osp.join(self._save_dir, VocPath.LABELMAP_FILE) write_label_map(path, self._label_map) - @staticmethod - def _strip_label(label): - return label.lower().strip() - def _load_categories(self, label_map_source=None): if label_map_source == LabelmapType.voc.name: - # strictly use VOC default labelmap + # use the default VOC colormap label_map = make_voc_label_map() - elif label_map_source == LabelmapType.source.name: - # generate colormap from the input dataset + elif label_map_source == LabelmapType.source.name and \ + AnnotationType.mask not in self._extractor.categories(): + # generate colormap for input labels labels = self._extractor.categories() \ .get(AnnotationType.label, LabelCategories()) label_map = OrderedDict() label_map['background'] = [None, [], []] for item in labels.items: - label_map[self._strip_label(item.name)] = [None, [], []] + label_map[item.name] = [None, [], []] + + elif label_map_source == LabelmapType.source.name and \ + AnnotationType.mask in self._extractor.categories(): + # use source colormap + labels = self._extractor.categories()[AnnotationType.label] + colors = self._extractor.categories()[AnnotationType.mask] + label_map = OrderedDict() + has_black = False + for idx, item in enumerate(labels.items): + color = colors.colormap.get(idx) + if idx is not None: + if color == (0, 0, 0): + has_black = True + label_map[item.name] = [color, [], []] + if not has_black and 'background' not in label_map: + label_map['background'] = [(0, 0, 0), [], []] + label_map.move_to_end('background', last=False) elif label_map_source in [LabelmapType.guess.name, None]: # generate colormap for union of VOC and input dataset labels @@ -436,11 +449,10 @@ class _Converter: source_labels = self._extractor.categories() \ .get(AnnotationType.label, LabelCategories()) for label in source_labels.items: - label_name = self._strip_label(label.name) - if label_name not in label_map: + if label.name not in label_map: rebuild_colormap = True - if label.attributes or label_name not in label_map: - label_map[label_name] = [None, [], label.attributes] + if label.attributes or label.name not in label_map: + label_map[label.name] = [None, [], label.attributes] if rebuild_colormap: for item in label_map.values(): @@ -469,27 +481,26 @@ class _Converter: self._label_id_mapping = self._make_label_id_map() def _is_label(self, s): - return self._label_map.get(self._strip_label(s)) is not None + return self._label_map.get(s) is not None def _is_part(self, s): - s = self._strip_label(s) for label_desc in self._label_map.values(): if s in label_desc[1]: return True return False def _is_action(self, label, s): - return self._strip_label(s) in self._get_actions(label) + return s in self._get_actions(label) def _get_actions(self, label): - label_desc = self._label_map.get(self._strip_label(label)) + label_desc = self._label_map.get(label) if not label_desc: return [] return label_desc[2] def _make_label_id_map(self): source_labels = { - id: self._strip_label(label.name) for id, label in + id: label.name for id, label in enumerate(self._extractor.categories().get( AnnotationType.label, LabelCategories()).items) } diff --git a/datumaro/datumaro/util/mask_tools.py b/datumaro/datumaro/util/mask_tools.py index a4eb8150..680093d9 100644 --- a/datumaro/datumaro/util/mask_tools.py +++ b/datumaro/datumaro/util/mask_tools.py @@ -9,6 +9,12 @@ from datumaro.util.image import lazy_image, load_image def generate_colormap(length=256): + """ + Generates colors using PASCAL VOC algorithm. + + Returns index -> (R, G, B) mapping. + """ + def get_bit(number, index): return (number >> index) & 1 diff --git a/datumaro/tests/test_voc_format.py b/datumaro/tests/test_voc_format.py index 9703acaa..d65113f0 100644 --- a/datumaro/tests/test_voc_format.py +++ b/datumaro/tests/test_voc_format.py @@ -708,7 +708,7 @@ class VocConverterTest(TestCase): SrcExtractor(), VocConverter(label_map='guess'), test_dir, target_dataset=DstExtractor()) - def test_dataset_with_source_labelmap(self): + def test_dataset_with_source_labelmap_undefined(self): class SrcExtractor(TestExtractorBase): def __iter__(self): yield DatasetItem(id=1, annotations=[ @@ -718,7 +718,7 @@ class VocConverterTest(TestCase): def categories(self): label_cat = LabelCategories() - label_cat.add('Label_1') # should become lowercase + label_cat.add('Label_1') label_cat.add('label_2') return { AnnotationType.label: label_cat, @@ -727,7 +727,7 @@ class VocConverterTest(TestCase): class DstExtractor(TestExtractorBase): def __iter__(self): yield DatasetItem(id=1, annotations=[ - Bbox(2, 3, 4, 5, label=self._label('label_1'), + Bbox(2, 3, 4, 5, label=self._label('Label_1'), id=1, group=1, attributes={ 'truncated': False, 'difficult': False, @@ -746,7 +746,7 @@ class VocConverterTest(TestCase): def categories(self): label_map = OrderedDict() label_map['background'] = [None, [], []] - label_map['label_1'] = [None, [], []] + label_map['Label_1'] = [None, [], []] label_map['label_2'] = [None, [], []] return VOC.make_voc_categories(label_map) @@ -755,6 +755,52 @@ class VocConverterTest(TestCase): SrcExtractor(), VocConverter(label_map='source'), test_dir, target_dataset=DstExtractor()) + def test_dataset_with_source_labelmap_defined(self): + class SrcExtractor(TestExtractorBase): + def __iter__(self): + yield DatasetItem(id=1, annotations=[ + Bbox(2, 3, 4, 5, label=0, id=1), + Bbox(1, 2, 3, 4, label=2, id=2), + ]) + + def categories(self): + label_map = OrderedDict() + label_map['label_1'] = [(1, 2, 3), [], []] + label_map['background'] = [(0, 0, 0), [], []] # can be not 0 + label_map['label_2'] = [(3, 2, 1), [], []] + return VOC.make_voc_categories(label_map) + + class DstExtractor(TestExtractorBase): + def __iter__(self): + yield DatasetItem(id=1, annotations=[ + Bbox(2, 3, 4, 5, label=self._label('label_1'), + id=1, group=1, attributes={ + 'truncated': False, + 'difficult': False, + 'occluded': False, + } + ), + Bbox(1, 2, 3, 4, label=self._label('label_2'), + id=2, group=2, attributes={ + 'truncated': False, + 'difficult': False, + 'occluded': False, + } + ), + ]) + + def categories(self): + label_map = OrderedDict() + label_map['label_1'] = [(1, 2, 3), [], []] + label_map['background'] = [(0, 0, 0), [], []] + label_map['label_2'] = [(3, 2, 1), [], []] + return VOC.make_voc_categories(label_map) + + with TestDir() as test_dir: + self._test_save_and_load( + SrcExtractor(), VocConverter(label_map='source'), + test_dir, target_dataset=DstExtractor()) + def test_dataset_with_fixed_labelmap(self): class SrcExtractor(TestExtractorBase): def __iter__(self):