Skip to content

Commit a12910d

Browse files
AIAA Client GA Release (#15)
* Support NVIDIA Segmentation+Annotation in AIAA Client+MITK Workbench * Add and show extreme points into pointset interaction * Fix URL for /models * PY Client segmentation API + Test data * Add Segmentation API example into tools * Support label and type in ListModels tool * support model type as optional field in models() API * Fix UI refresh for MITK plugin * Support model name instead of labels (design review) + fix color map for multi-labeled masks * Support label based model filtering config + correct tooltips
1 parent c58b579 commit a12910d

30 files changed

Lines changed: 843 additions & 237 deletions

cpp-client/include/nvidia/aiaa/client.h

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ class AIAA_CLIENT_API Client {
7070
*/
7171
ModelList models() const;
7272

73+
/*!
74+
@brief This API is used to fetch all the possible Models support by AIAA Server for matching label and model type
75+
@return ModelList object representing a list of Models
76+
77+
@throw nvidia.aiaa.error.101 in case of connect error
78+
@throw nvidia.aiaa.error.102 if case of response parsing
79+
*/
80+
ModelList models(const std::string &label, const Model::ModelType type) const;
81+
7382
/*!
7483
@brief This API is used to sample the input image
7584
@param[in] model Model to be used
@@ -105,26 +114,25 @@ class AIAA_CLIENT_API Client {
105114
const std::string &outputImageFile, ImageInfo &imageInfo) const;
106115

107116
/*!
108-
@brief 3D image segmentation over sampled input image and PointSet
109-
@param[in] model Model to be used
110-
@param[in] pointSet PointSet object which represents a set of points in 3-Dimensional for the organ. Minimum Client::MIN_POINTS_FOR_SEGMENTATION are expected
117+
@brief 3D image segmentation/annotation over sampled input image and PointSet depending on input Model
118+
@param[in] model Model to be used (it can be for segmentation or annotation)
119+
@param[in] pointSet PointSet object which represents a set of points in 3-Dimensional for the organ.
111120
@param[in] dimension Dimension for Input Image
112121
@param[in] inputImageFile Sampled Input image filename where image is stored in itk::Image<unsigned short, *> format
113122
@param[in] outputImageFile File name to store 3D binary mask image result from AIAA server in itk::Image<unsigned char, *> format
123+
@param[in] imageInfo Optional Original ImageInfo to recover in case of annotation models after inference
114124
115-
@retval 0 Success
116-
@retval -1 Insufficient Points in the input
117-
@retval -2 Input Model name is empty
125+
@retval New/Updated Pointset in case of segmentation which represents a set of points in 3-Dimensional for the organ.
118126
119127
@throw nvidia.aiaa.error.101 in case of connect error
120128
@throw nvidia.aiaa.error.102 if case of response parsing
121129
@throw nvidia.aiaa.error.103 if case of ITK error related to image processing
122130
*/
123-
int segmentation(const Model &model, const PointSet &pointSet, const std::string &inputImageFile, int dimension, const std::string &outputImageFile,
124-
const ImageInfo &imageInfo) const;
131+
PointSet segmentation(const Model &model, const PointSet &pointSet, const std::string &inputImageFile, int dimension, const std::string &outputImageFile,
132+
const ImageInfo &imageInfo = ImageInfo()) const;
125133

126134
/*!
127-
@brief 3D image segmentation using DEXTR3D method (this combines sampling + segmentation into single operation for 3D images)
135+
@brief 3D image annotation using DEXTR3D method (this combines sampling + segmentation into single operation for 3D images)
128136
@param[in] model Model to be used
129137
@param[in] pointSet PointSet object which represents a set of points in 3-Dimensional for the organ. Minimum Client::MIN_POINTS_FOR_SEGMENTATION are expected
130138
@param[in] inputImageFile Input image filename where image is stored in itk::Image<?, 3> format

cpp-client/include/nvidia/aiaa/model.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ struct AIAA_CLIENT_API Model {
6666
/// Image ROI size used while training [x,y,z,w] Format; This shall be used for dextr3D API
6767
std::vector<int> roi;
6868

69+
enum ModelType {
70+
annotation,
71+
segmentation,
72+
unknown
73+
};
74+
75+
/// Type of Model (segmentation/annotation)
76+
ModelType type;
77+
78+
/// Version of Model
79+
std::string version;
80+
6981
/*!
7082
@brief create Model from JSON String
7183
@param[in] json JSON String.
@@ -104,9 +116,16 @@ struct AIAA_CLIENT_API ModelList {
104116
First preference goes to exact match. Otherwise prefix match will be preferred.
105117
@note Here, the matching type is case-insensitive
106118
@param[in] label Organ Name
119+
@param[in] type Model Type
107120
@return Model
108121
*/
109-
Model getMatchingModel(const std::string &label);
122+
Model getMatchingModel(const std::string &label, Model::ModelType type = Model::annotation);
123+
124+
/// Checks if Polygons list is empty
125+
bool empty() const;
126+
127+
/// Count of Polygons list
128+
size_t size() const;
110129

111130
/*!
112131
@brief create Model from JSON String

cpp-client/src/client.cpp

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ namespace aiaa {
3737

3838
const std::string EP_MODELS = "/models";
3939
const std::string EP_DEXTRA_3D = "/dextr3d";
40-
const std::string EP_MASK_2_POLYGON = "/mask2polygon";
40+
const std::string EP_SEGMENTATION = "/segmentation";
41+
const std::string EP_MASK_TO_POLYGON = "/mask2polygon";
4142
const std::string EP_FIX_POLYGON = "/fixpolygon";
4243
const std::string IMAGE_FILE_EXTENSION = ".nii.gz";
4344

@@ -58,6 +59,16 @@ ModelList Client::models() const {
5859
return ModelList::fromJson(CurlUtils::doGet(uri, timeoutInSec));
5960
}
6061

62+
ModelList Client::models(const std::string &label, const Model::ModelType type) const {
63+
std::string uri = serverUri + EP_MODELS + "?label=" + label;
64+
if (type != Model::unknown) {
65+
uri += std::string("&type=") + (type == Model::segmentation ? "segmentation" : "annotation");
66+
}
67+
AIAA_LOG_DEBUG("URI: " << uri);
68+
69+
return ModelList::fromJson(CurlUtils::doGet(uri, timeoutInSec));
70+
}
71+
6172
template<typename TPixel>
6273
PointSet samplingT(const Model &model, const PointSet &pointSet, void *inputImage, int dimension, const std::string &outputImageFile,
6374
ImageInfo &imageInfo) {
@@ -171,27 +182,21 @@ PointSet Client::sampling(const Model &model, const PointSet &pointSet, const st
171182
throw exception(exception::INVALID_ARGS_ERROR, "UnSupported Pixel Type (only basic types are supported)");
172183
}
173184

174-
int Client::segmentation(const Model &model, const PointSet &pointSet, const std::string &inputImageFile, int dimension,
175-
const std::string &outputImageFile, const ImageInfo &imageInfo) const {
185+
PointSet Client::segmentation(const Model &model, const PointSet &pointSet, const std::string &inputImageFile, int dimension,
186+
const std::string &outputImageFile, const ImageInfo &imageInfo /*= ImageInfo()*/) const {
176187
if (dimension != 3) {
177188
throw exception(exception::INVALID_ARGS_ERROR, "UnSupported Dimension (only 3D is supported)");
178189
}
179190
if (model.name.empty()) {
180191
AIAA_LOG_WARN("Selected model is EMPTY");
181-
return -2;
182-
}
183-
if (pointSet.points.size() < MIN_POINTS_FOR_SEGMENTATION) {
184-
std::string msg = "Insufficient Points; Minimum Points required for input PointSet: "
185-
+ Utils::lexical_cast<std::string>(MIN_POINTS_FOR_SEGMENTATION);
186-
AIAA_LOG_WARN(msg);
187-
throw exception(exception::INVALID_ARGS_ERROR, msg.c_str());
192+
throw exception(exception::INVALID_ARGS_ERROR, "Model is EMPTY");
188193
}
189194

190195
bool postProcess = imageInfo.empty() ? false : true;
191196
std::string tmpResultFile = postProcess ? (Utils::tempfilename() + IMAGE_FILE_EXTENSION) : outputImageFile;
192197
AIAA_LOG_DEBUG("TmpResultFile: " << tmpResultFile << "; PostProcess: " << postProcess);
193198

194-
std::string uri = serverUri + EP_DEXTRA_3D + "?model=" + model.name;
199+
std::string uri = serverUri + (model.type == Model::segmentation ? EP_SEGMENTATION : EP_DEXTRA_3D) + "?model=" + model.name;
195200
std::string paramStr = "{\"sigma\":" + Utils::lexical_cast<std::string>(model.sigma) + ",\"points\":\"" + pointSet.toJson() + "\"}";
196201
std::string response = CurlUtils::doPost(uri, paramStr, inputImageFile, tmpResultFile, timeoutInSec);
197202

@@ -200,7 +205,7 @@ int Client::segmentation(const Model &model, const PointSet &pointSet, const std
200205
ITKUtils3DUChar::imagePostProcess(tmpResultFile, outputImageFile, imageInfo);
201206
std::remove(tmpResultFile.c_str());
202207
}
203-
return 0;
208+
return PointSet::fromJson(response);
204209
}
205210

206211
int Client::dextr3D(const Model &model, const PointSet &pointSet, const std::string &inputImageFile, Pixel::Type pixelType,
@@ -237,7 +242,7 @@ int Client::dextr3D(const Model &model, const PointSet &pointSet, const std::str
237242
}
238243

239244
PolygonsList Client::maskToPolygon(int pointRatio, const std::string &inputImageFile) const {
240-
std::string uri = serverUri + EP_MASK_2_POLYGON;
245+
std::string uri = serverUri + EP_MASK_TO_POLYGON;
241246
std::string paramStr = "{\"more_points\":" + Utils::lexical_cast<std::string>(pointRatio) + "}";
242247

243248
AIAA_LOG_DEBUG("URI: " << uri);

cpp-client/src/model.cpp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Model::Model() {
4444
sigma = DEFAULT_SIGMA;
4545
padding = DEFAULT_PADDING;
4646
roi = {DEFAULT_ROI, DEFAULT_ROI, DEFAULT_ROI, DEFAULT_ROI}; // support up-to 4-Dimension
47+
type = ModelType::annotation;
4748
}
4849

4950
// {"labels": ["brain_tumor_core"], "internal name": "Dextr3dCroppedEngine", "description": "", "name": "Dextr3DBrainTC", "padding": 20.0 "roi": [128,128,128], "sigma": 3.0}
@@ -54,9 +55,8 @@ Model Model::fromJson(const std::string &json) {
5455

5556
Model model;
5657
model.name = j["name"].get<std::string>();
57-
model.internal_name = j["internal name"].get<std::string>();
58-
model.description = j.find("description") != j.end() ? j["description"].get<std::string>() : j["decription"].get<std::string>();
59-
// TODO:: Ask server to correct the spelling for description
58+
model.internal_name = j.find("internal name") != j.end() ? j["internal name"].get<std::string>() : std::string();
59+
model.description = j.find("description") != j.end() ? j["description"].get<std::string>() : std::string();
6060

6161
for (auto e : j["labels"]) {
6262
model.labels.insert(e.get<std::string>());
@@ -79,6 +79,10 @@ Model Model::fromJson(const std::string &json) {
7979
model.roi.push_back(model.roi[model.roi.size() - 1]);
8080
}
8181

82+
std::string type = j.find("type") != j.end() ? j["type"].get<std::string>() : std::string();
83+
model.type = type == "segmentation" ? ModelType::segmentation : ModelType::annotation;
84+
model.version = j.find("version") != j.end() ? j["version"].get<std::string>() : std::string();
85+
8286
return model;
8387
} catch (nlohmann::json::parse_error& e) {
8488
AIAA_LOG_ERROR(e.what());
@@ -98,6 +102,8 @@ std::string Model::toJson(int space) const {
98102
j["sigma"] = sigma;
99103
j["padding"] = padding;
100104
j["roi"] = roi;
105+
j["type"] = (type == Model::segmentation ? "segmentation" : "annotation");
106+
j["version"] = version;
101107

102108
std::string str = j.dump(space);
103109
if (!space) {
@@ -138,9 +144,13 @@ std::string ModelList::toJson(int space) const {
138144
return space ? j.dump(space) : j.dump();
139145
}
140146

141-
Model ModelList::getMatchingModel(const std::string &labelName) {
147+
Model ModelList::getMatchingModel(const std::string &labelName, Model::ModelType type/* = Model::annotation*/) {
142148
// Exact Match (first preference)
143149
for (auto model : models) {
150+
if (model.type != type) {
151+
continue;
152+
}
153+
144154
for (auto label : model.labels) {
145155
AIAA_LOG_DEBUG("Exact Match: [" << label << "] vs [" << labelName << "]");
146156
if (Utils::iequals(labelName, label)) {
@@ -152,6 +162,10 @@ Model ModelList::getMatchingModel(const std::string &labelName) {
152162
// Prefix Match (find as prefix in either)
153163
std::string l1 = Utils::to_lower(labelName);
154164
for (auto model : models) {
165+
if (model.type != type) {
166+
continue;
167+
}
168+
155169
for (auto label : model.labels) {
156170
std::string l2 = Utils::to_lower(label);
157171
AIAA_LOG_DEBUG("Exact Match: [" << l2 << "] vs [" << l1 << "]");
@@ -164,6 +178,15 @@ Model ModelList::getMatchingModel(const std::string &labelName) {
164178
return Model();
165179
}
166180

181+
bool ModelList::empty() const {
182+
return models.empty();
183+
}
184+
185+
size_t ModelList::size() const {
186+
return models.size();
187+
}
188+
189+
167190
}
168191
}
169192

cpp-client/tools/CMakeLists.txt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ target_link_libraries(nvidiaAIAAListModels NvidiaAIAAClient ${CMAKE_DL_LIBS})
5252
add_executable(nvidiaAIAADEXTR3D aiaa/aiaa-dextra3d.cpp)
5353
target_link_libraries(nvidiaAIAADEXTR3D NvidiaAIAAClient ${CMAKE_DL_LIBS})
5454

55+
add_executable(nvidiaAIAASegmentation aiaa/aiaa-segmentation.cpp)
56+
target_link_libraries(nvidiaAIAASegmentation NvidiaAIAAClient ${CMAKE_DL_LIBS})
57+
5558
add_executable(nvidiaAIAASampling3D aiaa/aiaa-sampling3d.cpp)
5659
target_link_libraries(nvidiaAIAASampling3D NvidiaAIAAClient ${CMAKE_DL_LIBS})
5760

@@ -63,10 +66,11 @@ target_link_libraries(nvidiaAIAAMaskPolygon NvidiaAIAAClient ${CMAKE_DL_LIBS})
6366

6467

6568
# Install Targets
66-
install(TARGETS DicomToNifti DESTINATION bin)
67-
install(TARGETS NiftiToDicom DESTINATION bin)
69+
install(TARGETS DicomToNifti DESTINATION bin)
70+
install(TARGETS NiftiToDicom DESTINATION bin)
6871

69-
install(TARGETS nvidiaAIAAListModels DESTINATION bin)
70-
install(TARGETS nvidiaAIAADEXTR3D DESTINATION bin)
71-
install(TARGETS nvidiaAIAAFixPolygon DESTINATION bin)
72-
install(TARGETS nvidiaAIAAMaskPolygon DESTINATION bin)
72+
install(TARGETS nvidiaAIAAListModels DESTINATION bin)
73+
install(TARGETS nvidiaAIAADEXTR3D DESTINATION bin)
74+
install(TARGETS nvidiaAIAASegmentation DESTINATION bin)
75+
install(TARGETS nvidiaAIAAFixPolygon DESTINATION bin)
76+
install(TARGETS nvidiaAIAAMaskPolygon DESTINATION bin)

cpp-client/tools/aiaa/aiaa-model.cpp

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,21 @@
3434
int main(int argc, char **argv) {
3535
if (cmdOptionExists(argv, argv + argc, "-h")) {
3636
std::cout << "Usage:: <COMMAND> <OPTIONS>\n"
37-
" |-h (Help) Print this information |\n"
38-
" |-server Server URI {default: http://10.110.45.66:5000/v1} |\n"
39-
" |-label Find Matching Model for this label; If absent, output full Model List |\n"
40-
" |-output Output File Name to store result |\n"
41-
" |-format Format Output Json |\n"
42-
" |-timeout Timeout In Seconds {default: 60} |\n"
43-
" |-ts Print API Latency |\n";
37+
" |-h (Help) Print this information |\n"
38+
" |-server Server URI {default: http://10.110.45.66:5000/v1} |\n"
39+
" |-label Find Matching Model for this label; If absent, output full Model List |\n"
40+
" |-type Find Matching Model of type (segmentation/annotation) |\n"
41+
" |-output Output File Name to store result |\n"
42+
" |-format Format Output Json |\n"
43+
" |-timeout Timeout In Seconds {default: 60} |\n"
44+
" |-ts Print API Latency |\n";
4445

4546
return 0;
4647
}
4748

4849
std::string serverUri = getCmdOption(argv, argv + argc, "-server", "http://10.110.45.66:5000/v1");
4950
std::string label = getCmdOption(argv, argv + argc, "-label");
51+
std::string type = getCmdOption(argv, argv + argc, "-type");
5052
std::string outputJsonFile = getCmdOption(argv, argv + argc, "-output");
5153
int jsonSpace = cmdOptionExists(argv, argv + argc, "-format") ? 2 : 0;
5254
int timeout = nvidia::aiaa::Utils::lexical_cast<int>(getCmdOption(argv, argv + argc, "-timeout", "60"));
@@ -55,24 +57,15 @@ int main(int argc, char **argv) {
5557
try {
5658
auto begin = std::chrono::high_resolution_clock::now();
5759
nvidia::aiaa::Client client(serverUri, timeout);
58-
nvidia::aiaa::ModelList modelList = client.models();
5960

60-
auto end = std::chrono::high_resolution_clock::now();
61-
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
61+
nvidia::aiaa::Model::ModelType mt = nvidia::aiaa::Model::unknown;
62+
mt = type == "segmentation" ? nvidia::aiaa::Model::segmentation : mt;
63+
mt = type == "annotation" ? nvidia::aiaa::Model::annotation : mt;
6264

63-
if (!label.empty()) {
64-
nvidia::aiaa::Model model = modelList.getMatchingModel(label);
65-
if (outputJsonFile.empty()) {
66-
std::cout << model.toJson(jsonSpace) << std::endl;
67-
} else {
68-
stringToFile(model.toJson(jsonSpace), outputJsonFile);
69-
}
65+
nvidia::aiaa::ModelList modelList = type.empty() && label.empty() ? client.models() : client.models(label, mt);
7066

71-
if (printTs) {
72-
std::cout << "API Latency (in milli sec): " << ms << std::endl;
73-
}
74-
return 0;
75-
}
67+
auto end = std::chrono::high_resolution_clock::now();
68+
auto ms = std::chrono::duration_cast < std::chrono::milliseconds > (end - begin).count();
7669

7770
if (outputJsonFile.empty()) {
7871
std::cout << modelList.toJson(jsonSpace) << std::endl;

0 commit comments

Comments
 (0)