最近在作一個目標檢測算法,訓練時用到了 bootstrap 策略,因而我將 PASCAL 的 Ground Truth 格式的讀取函數從 Matlab 改寫爲 C++。PASCAL 的標註格式爲:html
# PASCAL Annotation Version 1.00
Image filename : "對應圖片路徑"
Image size (X x Y x C) : 寬 x 高 x 3
Database : "數據庫名稱"
Objects with ground truth : 1 { "PASperson" }
# Note that there might be other objects in the image
# for which ground truth data has not been provided.
# Top left pixel co-ordinates : (0, 0)
# Details for object 1 ("PASperson")
# Center point -- not available in other PASCAL databases -- refers
# to person head center
Original label for object 1 "PASperson" : "UprightPerson"
Center point on object 1 "PASperson" (X, Y) : (257, 187)
Bounding box for object 1 "PASperson" (Xmin, Ymin) - (Xmax, Ymax) : (195, 154) - (297, 468)
我寫的函數以下:算法
#include "stdio.h" #include "string.h" #include "stdlib.h" // object bounding rect struct GtRect { int x_min; int y_min; int x_max; int y_max; }; // ground truth of one image struct GtRecord { char* image_name; GtRect* objs; int obj_num; int height; int width; int channels; }; // return true if c is in char set s int _is_chars(char c, const char* s, int n) { for (int i = 0; i != n; ++i) { if (s[i] == c) { return 1; } } return 0; } void _trim_l(char* inout, const char* s) { int len = strlen(inout); int s_len = strlen(s); int i = 0; for (;i != len; ++i) { if (!_is_chars(inout[i], s, s_len)) { break; } } int d = i; int new_len = len - d; for (i = 0; i != new_len; ++i) { inout[i] = inout[i + d]; } inout[new_len] = '\0'; } void _trim_r(char* inout, const char* s) { int len = strlen(inout); int s_len = strlen(s); int i = len - 1; for (;i != -1; --i) { if (!_is_chars(inout[i], s, s_len)) { break; } } inout[i + 1] = '\0'; } inline void _trim_lr(char* inout, const char* s) { _trim_l(inout, s); _trim_r(inout, s); } // read ground truth (pascal format) //************************************ // Name: gt_pascal_read // Returns: GtRecord // const char * path : groundtruth file path //************************************ GtRecord gt_pascal_read(const char* path) { GtRecord ret = {0, 0, 0, 0, 0, 0}; FILE* f; fopen_s(&f, path, "r"); int obj_num = 0; int len = 0; GtRect rct; while (fgets(BUF1, 5000, f) != 0) { int match_type = _match_attr(BUF1); switch (match_type) { case 0: // read image filename sscanf_s(BUF1, _GT_ATTR[0], BUF2, 500); _trim_lr(BUF2, "\n\" "); len = strlen(BUF2); ret.image_name = (char*)malloc(len + 1); memcpy(ret.image_name, BUF2, len + 1); break; case 1: // read image size, channel sscanf_s(BUF1, _GT_ATTR[1], &ret.width, &ret.height, &ret.channels); break; case 2: // ignore database name break; case 3: sscanf_s(BUF1, _GT_ATTR[3], &rct.x_min, &rct.y_min, &rct.x_max, &rct.y_max); OBJ_BUF[obj_num++] = rct; break; case 4: // ignore polygon case 5: // ignore pixel map case 6: // ignore label break; } } fclose(f); ret.obj_num = obj_num; if (obj_num > 0) { ret.objs = (GtRect*)malloc(sizeof(GtRect) * obj_num); memcpy(ret.objs, OBJ_BUF, obj_num * sizeof(GtRect)); } return ret; } // release pascal ground truth //************************************ // Name: gt_pascal_release // Returns: void // GtRecord * r //************************************ void gt_pascal_release(GtRecord* r) { free(r->image_name); free(r->objs); r->image_name = 0; r->objs = 0; r->width = 0; r->height = 0; r->channels = 0; r->obj_num = 0; }
gt_pascal_read 函數忽略了 groundtruth 文件中的一些屬性,例如數據庫名稱等,若是要加回,能夠在函數的幾個空的 case 中添加便可。PASCAL 官方提供了幾個有用的 Matlab 腳本用於讀取和生成這樣的 groundtruth 文件,在算法開發的過程當中要多利用這樣的工具。數據庫