$ v) _; x) q I; l . y, S" }! { J& ~- B附注。作者在5月份更新了代码,现在最新版本号是1.3.0,博主亲测,源码在Windows 10和Ubuntu 16.04上正常运行。$ Q% o) f5 R b) K
6 e- o9 X- @2 V. l8 l 8 w8 C) |% o; `4 ] ]# y具体的安装查看Github教程:https://github.com/wkentaro/labelme/#installation8 x l6 v/ c* }1 }- I
0 F/ u- C; T9 T3 s! G
8 J+ S5 r; L, r8 J4 r' L @( y+ J8 x在原作者的github下载源码:https://github.com/tzutalin/labelImg! M- Y' E( q3 T4 R
。解压名为labelImg-master的文件夹,进入当前目录的命令行窗口,输入如下语句依次打开软件。 H0 x5 P2 o' S3 f) k5 h$ U
3 J% s- J: F, n, ~! Q& \; L; G
" d0 a- Y; ~0 U7 q2 e
python labelImg.py ; G6 @, l$ z9 \) j3 Q$ J( Q. S1 - ?3 X3 ^2 P" k3 @) @ 7 g, w/ l4 A) w& B* Q' f3 \ 9 w; D1 E* t; \2 c, P 5 G+ f5 v3 g) G1 S9 F9 w, I; | # Z+ k6 D5 m$ D; Q5 `% _具体使用* ~" h; ?3 V: d' C
修改默认的XML文件保存位置,使用快捷键“Ctrl+R”,更改为自定义位置,这里的路径一定不能包含中文,否则不会保存。 6 ^/ B( Q% S- u$ {, }& h! P( E, d# _
3 o& i2 q# Q& ^& O" n3 n" z- }& P
使用notepad++打开源文件夹中的data/predefined_classes.txt,修改默认分类,如person、car、motorcycle这三个分类。' G! m. q% _5 b( ~3 d+ f- Z% L
7 n& O0 `* \6 v$ P* D$ o3 ?4 H9 f* c8 a
“打开目录”打开图片文件夹,选择第一张图片开始标注,用“创建矩形框”或“Ctrl+N”启动框,点击结束框,双击选择类别。完成一张图片点击“保存”保存后,XML文件已经保存到本地了。单击“下一张图片”转到下一张图片。( a$ l2 E' I6 {6 @3 G% Z$ b+ m- y
, a# [7 A3 Q+ \8 u, i3 U 2 G8 o7 H: H- F9 ^- A贴标过程可以随时返回修改,保存的文件会覆盖上一个。- w" M' v) d2 k9 p
% K$ I* O' H) P- P. Z! ?$ l$ X2 v) f
完成注解后,打开XML文件,发现和PASCAL VOC格式一样。4 _9 O* J& `+ X
: M" e. o. T9 _0 _8 j. g0 T! Q' g% |
* g: G- P& \5 L! H2 _
将xml文件提取图像信息, C: F) k O6 g
下面列举如何将xml文件提取图像信息,图片保存到image文件夹,xml保存标注内容。图片和标注的文件名字一样的。' p* x" {& [4 K# o) }; |2 @
( N4 a: A0 Z5 U3 f4 m; a0 j% B) |9 K6 q# T3 T
+ f! g( x. Y7 K' b% b) t3 J( s ; ~$ K/ j+ {' A3 J: n7 a1 X# q b' @下面是images图片中的一个。 2 L( I5 `/ O8 q- I3 e- B' \3 Z( @8 A1 Z. K% s! r+ y
; H' J7 o8 C/ O( b<annotation> 1 z: I. l7 G% b+ \) ? <folder>train</folder>5 W& {% Y( E) q' _( p3 Z% h8 C6 w9 s \1 y
<filename>apple_30.jpg</filename> 8 [0 R6 V9 t0 \0 h7 H7 c* f+ i <path>C:\tensorflow1\models\research\object_detection\images\train\apple_30.jpg</path>- H4 q$ m- V" F8 B1 i
<source>% v c+ b. @5 g& B7 S* u2 R2 E1 b* b
<database>Unknown</database>& {3 G5 G+ Y8 h. r0 A6 M: t& @5 F& q
</source>& U- ~ }1 c$ o8 ?! i
<size> ( J4 F- b, M7 ? <width>800</width> $ P6 q+ S4 V. } <height>800</height>$ K; Y5 R( `7 E; C e! ]- E
<depth>3</depth>$ S) z" t$ O0 R+ @4 r
</size>% U2 n; S# G5 L0 K# g. p- C
<segmented>0</segmented> - r$ f" \& T) g, | <object>( A3 l' O' p( _' F6 g
<name>apple</name> % |! w! x1 G* T; b# ~, V6 |; j <pose>Unspecified</pose> ' j# d8 }/ D! }, }& ? <truncated>0</truncated># n; S/ P# ^4 }% N* g z1 {
<difficult>0</difficult>8 v( l! _- \: t, L
<bndbox># x& F( ?3 i: W2 [3 J* q1 q
<xmin>254</xmin>' {" X* a4 U& l: r
<ymin>163</ymin>/ p3 `; V' T- s8 d W/ A- t
<xmax>582</xmax> y* D% L" Z% i$ r% e x( ?
<ymax>487</ymax>7 j7 J9 l8 P4 o& i% ]3 N) O4 `/ c
</bndbox>+ d6 ?) W0 k' p4 D& G' i6 b% o3 A
</object>5 V* e- r' U) U: o5 M2 P
<object> 6 y I3 e4 g0 ?* B7 G/ m1 g <name>apple</name> % b U/ m- y, `4 |. P( c7 N5 P p% B <pose>Unspecified</pose>$ |0 q6 j3 I- v, k4 o& Z9 I( U3 y
<truncated>0</truncated>4 R- O" P. t$ `5 Q& z+ ?$ }5 a
<difficult>0</difficult> * I6 C7 { \0 f/ S <bndbox> 4 Q* \8 _/ w) s5 u4 s3 _% I <xmin>217</xmin> 6 B& o4 j0 C. a, w) }8 G <ymin>448</ymin> - ], q/ s) |4 |+ p8 C9 V( k7 f+ M <xmax>535</xmax> # {& b i' U1 S, S* [. g <ymax>713</ymax> 1 }1 [& ^7 v9 E* q( ]8 x; ?" \. P </bndbox># [1 d2 a+ P. Z+ V; M; P! p
</object>+ \! o. J7 s8 ^& p2 F
<object> 8 i" f, Y! m" }+ a <name>apple</name> 6 K& {# @4 `& W: n& F% n <pose>Unspecified</pose>, Y) J- c: ], E' F' W( M, s$ Z/ F0 f
<truncated>1</truncated>* y" O; \5 Q( s; q; M' E8 t
<difficult>0</difficult> ! O: D: z5 _! w+ v* K <bndbox>, l* h1 a" V( c% G# K
<xmin>603</xmin>2 u5 ~! z; G; p! ]6 K$ \
<ymin>470</ymin>8 K/ e0 j- \5 F) H
<xmax>800</xmax>( }4 B- z$ m: q
<ymax>716</ymax>1 L& V* r# a; l- b& v
</bndbox>% i* L6 E5 A. m Z8 f! j6 e
</object>6 |: k( f0 c0 w1 Z& Y8 I2 Q
<object>) L! |7 x8 z$ m
<name>apple</name> 3 s2 v- g3 m5 m3 [4 ` <pose>Unspecified</pose>4 @, F6 o! k; w9 L, i2 ]! X" i
<truncated>0</truncated>2 w, e, W! T" f! Z( b
<difficult>0</difficult>+ m0 J6 n% j$ r* Y8 E1 A* e) X
<bndbox>; P2 l( D- v8 k, \! e( E+ J
<xmin>468</xmin> 6 ~) J; M! g: l. G- @; f) g% b; f# q <ymin>179</ymin> ; @1 Q1 \3 A) z& S) J <xmax>727</xmax>1 W' e: G- y5 l$ e: S
<ymax>467</ymax>. H0 o& ?1 l. f! o. R
</bndbox> + Q9 @. ^# W6 H </object> # W$ p8 f6 w; k; n9 w4 q/ v <object> 5 a7 P; j2 t) O <name>apple</name> 9 s2 Y! D0 b* N4 I <pose>Unspecified</pose>+ n; Q, X9 t. i: k9 y: C$ C
<truncated>1</truncated> 3 ~0 s4 [% W. K6 t5 p0 U8 y <difficult>0</difficult> ) E" j3 U. H7 c* M& o# d0 R$ l <bndbox>' h" Y3 _1 m/ M& e
<xmin>1</xmin>' H8 k2 G1 l( P% S- Q. r
<ymin>63</ymin>( c5 C5 j5 ?' C" g8 w( s8 t
<xmax>308</xmax> 9 h& R. P0 z& |+ Y8 a# e1 B$ h4 w) J <ymax>414</ymax> . S& F2 }& j% [! z/ O& j. J) o# j </bndbox>3 [" o. ?8 _, g9 c
</object> , h9 q/ G! j9 k</annotation>0 _7 ?$ j. B8 v1 [4 i, J: ^: W! K( [8 [
1: x8 l [# q- \7 }# f9 Q
2, z4 d, n5 F6 z6 y# x8 ~- Q
3 6 v7 h# J# @- H% K; b7 i4 9 F5 q: t; c9 Y! j' b5 R5 % o# I6 ^5 ]0 j1 _. V) ?- u6! L3 _+ y6 b# C" ~
7 3 ^& B) L" L, w$ \9 K n1 R88 F- X) `$ V% y, n3 d6 r
9* a7 g0 B" `; X. K) `# ]/ ]
10: p0 u$ `5 `& F* c) K
11/ G# R9 H. j2 c0 |- `% ]
12+ _) N0 ~; [6 D' e* K
13, Z) v1 L% T1 K) f- l' G( ?% @
14 & f$ I) g/ V5 H, }0 S' J15 - t( Y- Q. I! Z16, p: d7 k+ s. J
17 / l$ l1 C- U- w9 [18 + n/ k! E, m! b( }4 r( t6 N/ k19 2 [7 {+ p9 W* U& \5 i- p/ O) B; ?20 8 r) ~" T$ X$ ^! G0 Z& m+ e3 e. L21 & ^( v6 Z& D9 p$ c# z% {. t# A+ L' w22( M! m: s9 w5 B3 ]; J. I1 v b3 ^
23 # {3 B; n6 N, K5 f24' q% T0 v% M; K7 L: ?/ E
25 % m$ e+ }* ^/ m# B( U9 p# L, E. J26 1 w/ }2 w0 f# `4 }6 S27, W- y. \0 W' X' e/ u
28: [# {4 u8 o+ P& m5 P6 s/ R
293 ^; b& |$ e1 E u' ]8 S
30 L) E( |5 ?6 P0 v
31 7 p {% _- u# u; J* [1 v! n32" ]. a9 Y7 J; T, \9 | ] j
33 - r4 _( s/ b" c. O4 Q: X347 G! u L' }/ [( H4 V* x
35/ P! j5 Q ]8 n- B5 l$ w" y
364 m- _1 t% j& x; Q: G
37/ {( m- c9 f6 R) E* g3 U' f0 y. t
38 9 V# ~9 e2 a! b4 I# g( ^396 [1 J& f* t8 k
40( C, o& [ i! d/ `0 G8 l: d) x, D
41# J6 }8 q5 b5 b
42. a$ U7 e k- n" U n% s7 X
43 4 H9 M1 _6 f5 o) q' C9 N* R4 ^44 l( d, R% `& o& A( u" T
45' D$ z4 B' N; }. }" {
46+ t2 ?' s. o5 q+ L3 v. w
47) L6 M* p% {8 u" l. c8 M
48 : ~9 B+ A ?, Z0 ]. J49 2 h7 J) M% T( o3 T( v* z4 y509 F0 z, v7 f* {
51' V- d& q8 D3 \& F) t" N2 ?; M
52 - W6 Q; w7 ]3 x8 p% I' D53 ^6 j: q8 {1 {& z! m& V54 ; y+ P: L0 y% N( o7 O8 [55 ) A+ T3 a' e# {3 k. }+ a6 F, D) `56+ \4 G( F* p2 E; G) k$ e# p
57' _9 W; L1 Z0 X3 E( O
58 & G8 k9 g* N3 y- s" i$ d; S# ~59 2 g$ Y/ p. X/ y- z. h2 U60* Z5 z2 B( n% _" m+ K5 C
61 ( b F9 }2 ^& |. s) o4 V2 j62+ `* o: M. O" d2 a' l; R
63 $ q+ C0 C8 S8 p$ S# j4 d- {8 s64 ) U0 M( @- d7 r( t* k6 Q65 3 ~! V' e& N* b5 v5 P) s& q& X66 $ m" e1 M& B I* D a* s% G' u675 E0 n5 z7 S; l3 U
68: q% @2 I& S$ D5 l ]. e5 Z( U
69" B4 z/ l3 d7 J* ~2 p
70 + \6 A4 ^7 P# M/ \9 @* j: f' z71 " h6 v; C; _ `# Y. M72' I: X. \! Z a! I
73 % @: q& \- @: T$ F1 k743 b* i% [8 a0 O
将xml文件提取图像信息,主要使用xml和opencv,基于torch提取,代码比较凌乱。 . l1 [8 h* y! m+ ^ ) H3 W1 q' r0 k. ?1 t* |, o2 W9 o+ \0 G* w$ Z2 U4 i/ ]
import os " v' ?& l3 {& }; s4 r( rimport numpy as np; E2 @- e% Y4 g. w- D
import cv2 " f, c9 c h' _+ i4 z' Himport torch) g; ^6 E6 r! D4 B
import matplotlib.patches as patches/ {( h9 B3 d/ ]
import albumentations as A+ ?/ ?3 g8 ^4 w0 r) E
from albumentations.pytorch.transforms import ToTensorV2$ p- a$ n+ e# ^* l) c
from matplotlib import pyplot as plt9 i4 `7 k1 x- K; n1 Y4 d! s/ M* [
from torch.utils.data import Dataset1 F: d) v }! A; I; V9 c7 K; y
from xml.etree import ElementTree as et 3 u% i) S! k( c. X2 Rfrom torchvision import transforms as torchtrans9 T* N2 W% ~! ]. b6 P
. I3 }5 X% E. h) L$ {, P$ p4 { a f$ ~
# defining the files directory and testing directory% o; H |! G3 h: F
train_image_dir = 'train/train/image' % L3 r7 p# x& s7 `train_xml_dir = 'train/train/xml' 7 a6 C2 G; b: b# v6 h# test_image_dir = 'test/test/image'+ d6 o$ H0 L2 }9 t- j
# test_xml_dir = 'test/test/xml'5 C& @/ P! j+ a
' i! j3 o, l( _3 Y* U' h 0 x* z% E. p+ t sclass FruitImagesDataset(Dataset):* T1 _4 q. b5 a- S( q, q/ [0 D
5 T. ~$ l4 `' R7 ~( @: @& ~1 i/ X% s) ]
def __init__(self, image_dir, xml_dir, width, height, transforms=None):# E/ K! F; G0 E
self.transforms = transforms 7 z$ Y$ d5 X' l self.image_dir = image_dir 4 m8 k! H, i. S: h self.xml_dir = xml_dir ) c; W' d3 ^! V2 i$ ` self.height = height& @0 R' S3 G2 M5 k W
self.width = width* C) |7 L% i5 U* b% }
" m: T& C3 F, y# b2 a& n3 s' {1 m* [$ W: B
# sorting the images for consistency8 y& ]6 t! y$ r6 a- t" x* L
# To get images, the extension of the filename is checked to be jpg3 H% J* }" a; E( r
self.imgs = [image for image in os.listdir(self.image_dir) : w" R4 T5 q. x) H/ @ if image[-4:] == '.jpg']$ Y+ q6 i1 y- T
self.xmls = [xml for xml in os.listdir(self.xml_dir) % z2 K- b) g% j' O0 a# _ if xml[-4:] == '.xml'] 3 V" y# c/ q% L8 c) a% L 9 e' D! }5 A9 n2 i7 c6 j* C , s& t# b3 Q4 q9 X0 D4 y8 V, ] # classes: 0 index is reserved for background4 s0 @6 i( {1 ^- J9 e
self.classes = ['apple', 'banana', 'orange'] * N) ?! A( N* q5 C$ I* N& q3 k' b- y" ]/ S/ V9 U4 y' M
: C, g6 X3 Z8 D1 l/ k% p' X def __getitem__(self, idx): & m2 h/ L. k- b/ s1 `/ l; U- I3 K G- k, ^, o
4 ]7 h) z0 {5 t
img_name = self.imgs[idx] ! w+ U' x5 s9 q image_path = os.path.join(self.image_dir, img_name) : c, c+ o' K* B - S2 J! n" p9 \7 Z4 A & C" o8 l, C8 M: ^4 {5 _* q7 Q0 ` # reading the images and converting them to correct size and color/ m( b' ~+ V5 D6 c* A! y2 b4 ~
img = cv2.imread(image_path)1 U# M2 G- J( z" K. l) z8 Z0 ~
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astype(np.float32) 4 Z' G$ I3 v: ^. V) K1 x img_res = cv2.resize(img_rgb, (self.width, self.height), cv2.INTER_AREA)0 b2 H) ]( h9 t- ?+ V/ Q
# diving by 2552 p' L! O; u3 h
img_res /= 255.0! q, f3 q( J k- g: W1 ~3 i
A S" Y6 n8 y9 o) a
0 M+ p6 [& }7 m
# annotation file" s7 Z, _3 J! e1 [9 |+ |
annot_filename = img_name[:-4] + '.xml'. K0 E* e& f' a- O. s8 k2 g: v* S
annot_file_path = os.path.join(self.xml_dir, annot_filename) 3 F, n, K% y. v- G3 Y( g" f- u& { Q# [' G# V0 w
. W+ W3 p* y% ~* T$ i boxes = []" w: y8 Q9 f) v
labels = [] 9 [: [% K: J. e2 X: H# C& L tree = et.parse(annot_file_path)6 u- r# o z( l8 I
root = tree.getroot()4 W7 H% h) w! f) e
9 p5 U/ f3 v+ N- U0 L% F 7 |, h+ Q8 p: c; b0 K m4 v8 m. B1 M( A # cv2 image gives size as height x width8 F- _* e4 O' a( w5 C
wt = img.shape[1] 9 Y% z a/ [; Z: O, g/ f3 C7 X! a ht = img.shape[0]5 M: w. e6 J& m7 V
6 u6 Q) _! F# C2 a4 P
' {& u$ m) N8 s- U$ S- `
# box coordinates for xml files are extracted and corrected for image size given 8 y: N# d8 I/ k3 {7 E8 P: } for member in root.findall('object'):1 o6 z3 t- a- ]
labels.append(self.classes.index(member.find('name').text)) , X& `7 L9 N( p( R: }0 x( ?6 ~0 [9 @5 A+ @" J: ~1 s) W1 q
6 X0 D* _# d" l) C6 \
# bounding box- j3 v" G; u7 ]$ C
xmin = int(member.find('bndbox').find('xmin').text) $ n9 B& O+ y- D& B" E xmax = int(member.find('bndbox').find('xmax').text)- _! Y8 d6 [& ^( m( F% @
/ y1 s& w3 \9 M. @ & y, V# s. q9 Q # convert boxes into a torch.Tensor/ q6 B6 g2 Y3 u6 i5 G% s+ W
boxes = torch.as_tensor(boxes, dtype=torch.float32) : X; |: e9 A( q- L0 S% _! `8 Y ! z; E) G5 s8 X. K- z- u9 a3 f5 {2 r% |+ m5 J& \, Q7 b
# getting the areas of the boxes % ], g% J' u1 V area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0]) - U4 o x/ K7 d' X ; ?- D% L+ N5 w2 j8 i+ { w/ v% |; G
# suppose all instances are not crowd & \# `0 F' k; ~( w" m- i1 a iscrowd = torch.zeros((boxes.shape[0],), dtype=torch.int64) 0 ]4 _0 h' S6 |, h: |. [ ( P" P: S% X5 D- u: @' l+ L ' v5 b6 [% y. x, W5 O6 ^- y labels = torch.as_tensor(labels, dtype=torch.int64) h" \9 d& E) C: L ]) f* P" I5 z, d
% r# ?+ [' g3 p, c, D* B, K target = {}5 m' D% t! r! k# S9 u
target["boxes"] = boxes' V! f G I) U( p5 }. u
target["labels"] = labels % G2 N, [; @# c' p0 p+ q/ V target["area"] = area. U4 Q1 W. |5 `/ ]
target["iscrowd"] = iscrowd 9 w% u+ T2 G& a/ s( R) i. U # image_id% [& {, @# H+ s8 V2 d
image_id = torch.tensor([idx])* m7 _! {* }& r+ f7 q
target["image_id"] = image_id - H0 @/ D+ X2 C3 y , q0 g& I& K `* S' C! t: V' ?, ? 4 R& y/ a( m2 `3 n& z: ^% a if self.transforms:! J' l0 A6 j5 j6 d0 J, {/ k
sample = self.transforms(image=img_res, & s+ m: K4 Q. c6 k( f! y' R/ ~ bboxes=target['boxes'],% F, k: M; q& x( d+ K" l
labels=labels) " q& Z) Q* Z% [+ y o& c& I " N4 J- `/ S" u, a w' Q4 |! M8 R8 w) ~* v. a
img_res = sample['image']$ [! Y# G+ X& i
target['boxes'] = torch.Tensor(sample['bboxes'])5 [5 |, [$ [ y* B% y
( M; W5 |8 i9 F
; m9 R0 ?3 W3 ]) ^ Q' v return img_res, target& t4 t3 W. S9 Z3 W; T' k
, R/ }4 ^" g- j6 Z: \9 |; v7 q2 k' g8 y1 z# N
def __len__(self):" _, o$ P _+ b! d! U5 v. E/ z
return len(self.imgs) - y, @7 P. H4 `' @. l1 ]( K) S( R& o; c+ e* q: |8 S
+ z( q/ x) P7 }5 v1 h) Z# function to convert a torchtensor back to PIL image ; ^6 ~& l6 {/ }" G+ n, H# K) udef torch_to_pil(img): - j6 x+ c! Y/ t7 p. ^: _# U return torchtrans.ToPILImage()(img).convert('RGB') h; O) h; ^ ~: P5 H+ b! f- Z 5 Z3 n3 E) [) Q c s" u 0 o- T$ r- H/ l$ s 7 a! t8 s, R' U# ] k0 K% Q 6 ^" q1 g4 C9 Hdef plot_img_bbox(img, target):# ]' O8 q7 `9 h* m6 z' \. {
# plot the image and bboxes# M8 P5 g( R1 J2 V8 k- Z
fig, a = plt.subplots(1, 1) $ n& G0 f: _& p+ k8 H9 ` fig.set_size_inches(5, 5)7 f7 ?7 H5 b: G/ ~ P* @# w
a.imshow(img) + S* t2 \4 l3 W6 U. G for box in (target['boxes']): # v( l, b" G' j x, y, width, height = box[0], box[1], box[2] - box[0], box[3] - box[1] 1 S. ~2 V& E7 Y% T% b/ K% o: F/ s rect = patches.Rectangle((x, y), S& D3 R& a( h- F. l+ p width, height, 1 P' F X0 R# n( y7 N; N linewidth=2,0 V3 j$ Z) U0 E1 ]5 V* R8 V
edgecolor='r',5 B6 C# z# \% t. Q: e
facecolor='none')0 q5 o" p. K( ?
: `2 b% g7 \8 I% K1 X" P( r9 J k, u
# Draw the bounding box on top of the image; |$ o* v8 S' `7 N
a.add_patch(rect)/ u4 G7 [; l. \2 O
plt.show()* _ {7 W& \: B4 k
: h5 e6 h/ `, `9 s& g4 i
* U- X" Q1 e9 o$ X* Q1 i u) S+ l! w1 Z: S
1 V9 X4 Z# {5 Q& A7 J- u# Udef get_transform(train): ) P! U& V9 H* ~# A if train: + {! a& H! p/ b- V% U0 t0 v return A.Compose([" V: I& S" o. V4 W9 P* W/ Y, Y
A.HorizontalFlip(0.5), # Z+ _: M1 n& t # ToTensorV2 converts image to pytorch tensor without div by 2556 y0 J/ _3 d, _3 R
ToTensorV2(p=1.0)$ q: t6 T3 J {. O
], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})9 z0 ^1 Q" F- h- ~5 W
else: # r, Z3 j+ T+ ~ return A.Compose([ . F) y$ p. P9 x5 @! Y ToTensorV2(p=1.0) 5 m5 }5 Q+ b! g1 A ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']}) 7 J% _( V, P v+ X+ f5 D: N! ^) J0 ~9 o9 F- v* p! K