import os
import json
from PIL import Image
def load_file(fpath):#fpath是具體的文件 ,作用:#str to list
assert os.path.exists(fpath) #assert() raise-if-not
with open(fpath,'r') as fid:
lines = fid.readlines()
records = [json.loads(line.strip('\n')) for line in lines] #str to list
return records
def crowdhuman2coco(odgt_path, json_path, image_path):#一個輸入文件路徑,一個輸出文件路徑
records = load_file(odgt_path) #提取odgt文件數據
#預處理
json_dict = {"images":[], "annotations": [], "categories": []}#定義一個字典,coco數據集標註格式
START_B_BOX_ID = 1 #設定框的起始ID
image_id = 1 #每個image的ID唯一,自己設定start,每次++
bbox_id = START_B_BOX_ID
image = {} #定義一個字典,記錄image
annotation = {} #記錄annotation
categories = {} #進行類別記錄
record_list = len(records) #獲得record的長度,循環遍歷所有數據。
print(record_list)
#一行一行的處理。
for i in range(record_list):
file_name = records[i]['ID']+'.jpg' #這裏是字符串格式 eg.273278,600e5000db6370fb
#image_id = int(records[i]['ID'].split(",")[0]) 這樣會導致id唯一,要自己設定
c_file = os.path.join(image_path, file_name)
im = Image.open(c_file)
#根據文件名,獲取圖片,這樣可以獲取到圖片的寬高等信息。因爲再odgt數據集裏,沒有寬高的字段信息。
image = {'file_name': file_name, 'height': im.size[1], 'width': im.size[0],'id':image_id} #im.size[0],im.size[1]分別是寬高
json_dict['images'].append(image) #這一步完成一行數據到字典images的轉換。
gt_box = records[i]['gtboxes']
gt_box_len = len(gt_box) #每一個字典gtboxes裏,也有好幾個記錄,分別提取記錄。
for j in range(gt_box_len):
category = gt_box[j]['tag']
if category not in categories: #該類型不在categories,就添加上去
new_id = len(categories) + 1 #ID遞增
categories[category] = new_id
category_id = categories[category] #重新獲取它的類別ID
fbox = gt_box[j]['fbox'] #獲得全身框
#對ignore進行處理,ignore有時在key:extra裏,有時在key:head_attr裏。屬於互斥的。
ignore = 0 #下面key中都沒有ignore時,就設爲0,據觀察,都存在,只是存在哪個字典裏,需要判斷一下
if "ignore" in gt_box[j]['head_attr']:
ignore = gt_box[j]['head_attr']['ignore']
if "ignore" in gt_box[j]['extra']:
ignore = gt_box[j]['extra']['ignore']
#對字典annotation進行設值。
annotation = {'area': fbox[2]*fbox[3], 'iscrowd': ignore, 'image_id': #添加hbox、vbox字段。
image_id, 'bbox':fbox, 'hbox':gt_box[j]['hbox'],'vbox':gt_box[j]['vbox'],
'category_id': category_id,'id': bbox_id,'ignore': ignore,'segmentation': []}
#area的值,暫且就是fbox的寬高相乘了,觀察裏面的數據,發現fbox[2]小、fbox[3]很大,剛好一個全身框的寬很小,高就很大。(猜測),不是的話,再自行修改
#segmentation怎麼處理?博主自己也不知道,找不到對應的數據,這裏就暫且不處理。
#hbox、vbox、ignore是添加上去的,以防有需要。
json_dict['annotations'].append(annotation)
bbox_id += 1 #框ID ++
image_id += 1 #這個image_id的遞增操作,注意位置,博主一開始,放上面執行了,後面出了bug,自己可以理一下。
#annotations的轉化結束。
#下面這一步,對所有數據,只需執行一次,也就是對categories裏的類別進行統計。
for cate, cid in categories.items():
#dict.items()返回列表list的所有列表項,形如這樣的二元組list:[(key,value),(key,value),...]
cat = {'supercategory': 'none', 'id': cid, 'name': cate}
json_dict['categories'].append(cat)
#到此,json_dict的轉化全部完成,對於其他的key,
#因爲沒有用到(不訪問),就不需要給他們空間,也不需要去處理,字典是按key訪問的,如果自己需要就自己添加上去就行
json_fp = open(json_path, 'w')
json_str = json.dumps(json_dict) #寫json文件。
json_fp.write(json_str)
json_fp.close()
if __name__=='__main__':
odgt_path='./annotation_val.odgt'
json_path='./annotation_val.json'
image_path= './CrowdHuman_val/Images'
crowdhuman2coco(odgt_path, json_path, image_path)
說明:
tag:label名稱,有兩類:person及mask,mask是指這樣的情況:crowd, reflection, something like person, … 因此,一般我們只需要取出person即可。
hbox:頭部的BBox(Head)
head_attr:頭部的一些屬性
fbox:全身的BBox(Full body)
vbox:沒有被遮蔽的BBox(Visible body)
extra:BBox的額外屬性
以上持續重複tag, hbox…, extra,視影像中有多少人而定。
其中extra中有ignore,occ,unsure屬性。
ignore:這個去掉就好。
occ:遮擋
unsure:這個好像都是爲0,不去管他就好了。
Crowdhuman將雕塑、影子,鏡子裏的人等視爲ignore區域,此外有一類ignore區域是很難精準預測的密集人羣,
參考:https://chtseng.wordpress.com/2019/12/13/crowdhuman-dataset-%E4%BB%8B%E7%B4%B9/
https://zhuanlan.zhihu.com/p/68677880