@@ -72,7 +72,7 @@ cv::Mat convertStringValToMat(const std::string& stringVal) {
72
72
try {
73
73
return cv::imdecode (dataMat, cv::IMREAD_UNCHANGED);
74
74
} catch (const cv::Exception& e) {
75
- SPDLOG_ERROR (" Error during string_val to mat conversion: {}" , e.what ());
75
+ SPDLOG_DEBUG (" Error during string_val to mat conversion: {}" , e.what ());
76
76
return cv::Mat{};
77
77
}
78
78
}
@@ -89,79 +89,25 @@ Status convertPrecision(const cv::Mat& src, cv::Mat& dst, const ovms::Precision
89
89
}
90
90
91
91
Status validateLayout (const std::shared_ptr<TensorInfo>& tensorInfo) {
92
- if ((tensorInfo->getLayout () != " NHWC" ) &&
93
- (tensorInfo->getLayout () != Layout::getUnspecifiedLayout ()) && // handle DAG
94
- (tensorInfo->getLayout () != Layout::getDefaultLayout ())) { // handle model without Layout set
92
+ static const std::string binarySupportedLayout = " N...HWC" ;
93
+ if (!tensorInfo->getLayout ().createIntersection (Layout (binarySupportedLayout), tensorInfo->getShape ().size ()).has_value ()) {
94
+ SPDLOG_DEBUG (" Endpoint needs to be compatible with {} to support binary image inputs, actual: {}" ,
95
+ binarySupportedLayout,
96
+ tensorInfo->getLayout ());
95
97
return StatusCode::UNSUPPORTED_LAYOUT;
96
98
}
97
99
return StatusCode::OK;
98
100
}
99
101
100
- bool resizeNeeded (const cv::Mat& image, const std::shared_ptr<TensorInfo>& tensorInfo) {
101
- Dimension cols = Dimension::any ();
102
- Dimension rows = Dimension::any ();
103
- if (tensorInfo->getShape ().size () == 4 ) {
104
- cols = tensorInfo->getShape ()[2 ];
105
- rows = tensorInfo->getShape ()[1 ];
106
- } else if (tensorInfo->isInfluencedByDemultiplexer () && tensorInfo->getShape ().size () == 5 ) {
107
- cols = tensorInfo->getShape ()[3 ];
108
- rows = tensorInfo->getShape ()[2 ];
109
- } else {
110
- return false ;
111
- }
112
- if (cols.isAny ()) {
113
- cols = image.cols ;
114
- }
115
- if (rows.isAny ()) {
116
- rows = image.rows ;
117
- }
118
- if ((!cols.match (image.cols )) || (!rows.match (image.rows ))) {
102
+ bool resizeNeeded (const cv::Mat& image, const dimension_value_t height, const dimension_value_t width) {
103
+ if (height != image.rows || width != image.cols ) {
119
104
return true ;
120
105
}
121
106
return false ;
122
107
}
123
108
124
- Status resizeMat (const cv::Mat& src, cv::Mat& dst, const std::shared_ptr<TensorInfo>& tensorInfo) {
125
- Dimension cols = Dimension::any ();
126
- Dimension rows = Dimension::any ();
127
- if (tensorInfo->getShape ().size () == 4 ) {
128
- cols = tensorInfo->getShape ()[2 ];
129
- rows = tensorInfo->getShape ()[1 ];
130
- } else if (tensorInfo->isInfluencedByDemultiplexer () && tensorInfo->getShape ().size () == 5 ) {
131
- cols = tensorInfo->getShape ()[3 ];
132
- rows = tensorInfo->getShape ()[2 ];
133
- } else {
134
- return StatusCode::UNSUPPORTED_LAYOUT;
135
- }
136
- if (cols.isAny ()) {
137
- cols = src.cols ;
138
- }
139
- if (rows.isAny ()) {
140
- rows = src.rows ;
141
- }
142
- if (cols.isDynamic ()) {
143
- dimension_value_t value = src.cols ;
144
- if (src.cols < cols.getMinValue ())
145
- value = cols.getMinValue ();
146
-
147
- if (src.cols > cols.getMaxValue ())
148
- value = cols.getMaxValue ();
149
-
150
- if (value != src.cols )
151
- cols = Dimension (value);
152
- }
153
- if (rows.isDynamic ()) {
154
- dimension_value_t value = src.rows ;
155
- if (src.rows < rows.getMinValue ())
156
- value = rows.getMinValue ();
157
-
158
- if (src.rows > rows.getMaxValue ())
159
- value = rows.getMaxValue ();
160
-
161
- if (value != src.rows )
162
- rows = Dimension (value);
163
- }
164
- cv::resize (src, dst, cv::Size (cols.getStaticValue (), rows.getStaticValue ()));
109
+ Status resizeMat (const cv::Mat& src, cv::Mat& dst, const dimension_value_t height, const dimension_value_t width) {
110
+ cv::resize (src, dst, cv::Size (width, height));
165
111
return StatusCode::OK;
166
112
}
167
113
@@ -199,7 +145,7 @@ Status validateResolutionAgainstFirstBatchImage(const cv::Mat input, cv::Mat* fi
199
145
if (input.cols == firstBatchImage->cols && input.rows == firstBatchImage->rows ) {
200
146
return StatusCode::OK;
201
147
}
202
- SPDLOG_ERROR (" Each binary image in request needs to have resolution matched. First cols: {}, rows: {}, current cols: {}, rows: {}" ,
148
+ SPDLOG_DEBUG (" Each binary image in request needs to have resolution matched. First cols: {}, rows: {}, current cols: {}, rows: {}" ,
203
149
firstBatchImage->cols , firstBatchImage->rows , input.cols , input.rows );
204
150
return StatusCode::BINARY_IMAGES_RESOLUTION_MISMATCH;
205
151
}
@@ -212,14 +158,13 @@ bool checkBatchSizeMismatch(const std::shared_ptr<TensorInfo>& tensorInfo,
212
158
return !tensorInfo->getBatchSize ().value ().match (batchSize);
213
159
}
214
160
215
- Status validateInput (const std::shared_ptr<TensorInfo>& tensorInfo, const cv::Mat input, cv::Mat* firstBatchImage) {
216
- // For pipelines with only custom nodes entry, or models with default layout there is no way to deduce layout.
217
- // With unknown layout, there is no way to deduce pipeline input resolution.
218
- // This forces binary utility to create tensors with resolution inherited from input binary image from request.
219
- // To achieve it, in this specific case we require all binary images to have the same resolution.
220
- // TODO check if H/W is undefined and only then check this CVS-77193
221
- if (firstBatchImage &&
222
- (tensorInfo->getLayout () == Layout::getUnspecifiedLayout ())) {
161
+ Status validateInput (const std::shared_ptr<TensorInfo>& tensorInfo, const cv::Mat input, cv::Mat* firstBatchImage, bool enforceResolutionAlignment) {
162
+ // Binary inputs are supported for any endpoint that is compatible with N...HWC layout.
163
+ // With unknown layout, there is no way to deduce expected endpoint input resolution.
164
+ // This forces binary utility to create tensors with resolution inherited from first batch of binary input image (request).
165
+ // In case of any dimension in endpoint shape is dynamic, we need to validate images against first image resolution.
166
+ // Otherwise we can omit that, and proceed to image resize.
167
+ if (firstBatchImage && enforceResolutionAlignment) {
223
168
auto status = validateResolutionAgainstFirstBatchImage (input, firstBatchImage);
224
169
if (!status.ok ()) {
225
170
return status;
@@ -258,17 +203,90 @@ Status validateTensor(const std::shared_ptr<TensorInfo>& tensorInfo,
258
203
return StatusCode::OK;
259
204
}
260
205
206
+ Dimension getTensorInfoHeightDim (const std::shared_ptr<TensorInfo>& tensorInfo) {
207
+ size_t numberOfShapeDimensions = tensorInfo->getShape ().size ();
208
+ if (numberOfShapeDimensions < 4 || numberOfShapeDimensions > 5 ) {
209
+ throw std::logic_error (" wrong number of shape dimensions" );
210
+ }
211
+ size_t position = numberOfShapeDimensions == 4 ? /* NHWC*/ 1 : /* N?HWC*/ 2 ;
212
+ return tensorInfo->getShape ()[position];
213
+ }
214
+
215
+ Dimension getTensorInfoWidthDim (const std::shared_ptr<TensorInfo>& tensorInfo) {
216
+ size_t numberOfShapeDimensions = tensorInfo->getShape ().size ();
217
+ if (numberOfShapeDimensions < 4 || numberOfShapeDimensions > 5 ) {
218
+ throw std::logic_error (" wrong number of shape dimensions" );
219
+ }
220
+ size_t position = numberOfShapeDimensions == 4 ? /* NHWC*/ 2 : /* N?HWC*/ 3 ;
221
+ return tensorInfo->getShape ()[position];
222
+ }
223
+
224
+ void updateTargetResolution (Dimension& height, Dimension& width, const cv::Mat& image) {
225
+ if (height.isAny ()) {
226
+ height = image.rows ;
227
+ } else if (height.isDynamic ()) {
228
+ if (height.match (image.rows )) {
229
+ height = image.rows ;
230
+ } else {
231
+ if (image.rows > height.getMaxValue ()) {
232
+ height = height.getMaxValue ();
233
+ } else {
234
+ height = height.getMinValue ();
235
+ }
236
+ }
237
+ }
238
+ if (width.isAny ()) {
239
+ width = image.cols ;
240
+ } else if (width.isDynamic ()) {
241
+ if (width.match (image.cols )) {
242
+ width = image.cols ;
243
+ } else {
244
+ if (image.cols > width.getMaxValue ()) {
245
+ width = width.getMaxValue ();
246
+ } else {
247
+ width = width.getMinValue ();
248
+ }
249
+ }
250
+ }
251
+ }
252
+
253
+ bool isResizeSupported (const std::shared_ptr<TensorInfo>& tensorInfo) {
254
+ for (const auto & dim : tensorInfo->getShape ()) {
255
+ if (dim.isAny ()) {
256
+ return false ;
257
+ }
258
+ }
259
+ if (tensorInfo->getLayout () != " NHWC" &&
260
+ tensorInfo->getLayout () != " N?HWC" &&
261
+ tensorInfo->getLayout () != Layout::getUnspecifiedLayout ()) {
262
+ return false ;
263
+ }
264
+ return true ;
265
+ }
266
+
261
267
Status convertTensorToMatsMatchingTensorInfo (const tensorflow::TensorProto& src, std::vector<cv::Mat>& images, const std::shared_ptr<TensorInfo>& tensorInfo) {
268
+ Dimension targetHeight = getTensorInfoHeightDim (tensorInfo);
269
+ Dimension targetWidth = getTensorInfoWidthDim (tensorInfo);
270
+
271
+ // Enforce resolution alignment against first image in the batch if resize is not supported.
272
+ bool resizeSupported = isResizeSupported (tensorInfo);
273
+ bool enforceResolutionAlignment = !resizeSupported;
274
+
262
275
for (int i = 0 ; i < src.string_val_size (); i++) {
263
276
cv::Mat image = convertStringValToMat (src.string_val (i));
264
277
if (image.data == nullptr )
265
278
return StatusCode::IMAGE_PARSING_FAILED;
266
279
267
280
cv::Mat* firstImage = images.size () == 0 ? nullptr : &images.at (0 );
268
- auto status = validateInput (tensorInfo, image, firstImage);
281
+ auto status = validateInput (tensorInfo, image, firstImage, enforceResolutionAlignment );
269
282
if (status != StatusCode::OK) {
270
283
return status;
271
284
}
285
+
286
+ if (i == 0 ) {
287
+ updateTargetResolution (targetHeight, targetWidth, image);
288
+ }
289
+
272
290
if (!isPrecisionEqual (image.depth (), tensorInfo->getPrecision ())) {
273
291
cv::Mat imageCorrectPrecision;
274
292
status = convertPrecision (image, imageCorrectPrecision, tensorInfo->getPrecision ());
@@ -278,14 +296,26 @@ Status convertTensorToMatsMatchingTensorInfo(const tensorflow::TensorProto& src,
278
296
}
279
297
image = std::move (imageCorrectPrecision);
280
298
}
281
- if (resizeNeeded (image, tensorInfo)) {
299
+ if (!targetHeight.isStatic () || !targetWidth.isStatic ()) {
300
+ return StatusCode::INTERNAL_ERROR;
301
+ }
302
+ if (resizeNeeded (image, targetHeight.getStaticValue (), targetWidth.getStaticValue ())) {
303
+ if (!resizeSupported) {
304
+ return StatusCode::INVALID_SHAPE;
305
+ }
282
306
cv::Mat imageResized;
283
- status = resizeMat (image, imageResized, tensorInfo );
307
+ status = resizeMat (image, imageResized, targetHeight. getStaticValue (), targetWidth. getStaticValue () );
284
308
if (!status.ok ()) {
285
309
return status;
286
310
}
287
311
image = std::move (imageResized);
288
312
}
313
+
314
+ if (i == 0 && src.string_val_size () > 1 ) {
315
+ // TODO: CVS-78796 Check if the total bytes for tensor will not exceed 1GB.
316
+ // Multiply src.string_val_size() * image resolution * precision size
317
+ }
318
+
289
319
images.push_back (image);
290
320
}
291
321
@@ -304,7 +334,7 @@ shape_t getShapeFromImages(const std::vector<cv::Mat>& images, const std::shared
304
334
return dims;
305
335
}
306
336
307
- ov::Tensor createTensorFromMats (const std::vector<cv::Mat>& images, const std::shared_ptr<TensorInfo>& tensorInfo, bool isPipeline ) {
337
+ ov::Tensor createTensorFromMats (const std::vector<cv::Mat>& images, const std::shared_ptr<TensorInfo>& tensorInfo) {
308
338
ov::Shape shape = getShapeFromImages (images, tensorInfo);
309
339
ov::element::Type precision = tensorInfo->getOvPrecision ();
310
340
ov::Tensor tensor (precision, shape);
@@ -316,7 +346,7 @@ ov::Tensor createTensorFromMats(const std::vector<cv::Mat>& images, const std::s
316
346
return tensor;
317
347
}
318
348
319
- ov::Tensor convertMatsToTensor (std::vector<cv::Mat>& images, const std::shared_ptr<TensorInfo>& tensorInfo, bool isPipeline ) {
349
+ ov::Tensor convertMatsToTensor (std::vector<cv::Mat>& images, const std::shared_ptr<TensorInfo>& tensorInfo) {
320
350
switch (tensorInfo->getPrecision ()) {
321
351
case ovms::Precision::FP32:
322
352
case ovms::Precision::I32:
@@ -326,7 +356,7 @@ ov::Tensor convertMatsToTensor(std::vector<cv::Mat>& images, const std::shared_p
326
356
case ovms::Precision::FP16:
327
357
case ovms::Precision::U16:
328
358
case ovms::Precision::I16:
329
- return createTensorFromMats (images, tensorInfo, isPipeline );
359
+ return createTensorFromMats (images, tensorInfo);
330
360
case ovms::Precision::MIXED:
331
361
case ovms::Precision::Q78:
332
362
case ovms::Precision::BIN:
@@ -337,7 +367,7 @@ ov::Tensor convertMatsToTensor(std::vector<cv::Mat>& images, const std::shared_p
337
367
}
338
368
}
339
369
340
- Status convertStringValToTensor (const tensorflow::TensorProto& src, ov::Tensor& tensor, const std::shared_ptr<TensorInfo>& tensorInfo, bool isPipeline ) {
370
+ Status convertStringValToTensor (const tensorflow::TensorProto& src, ov::Tensor& tensor, const std::shared_ptr<TensorInfo>& tensorInfo) {
341
371
auto status = validateTensor (tensorInfo, src);
342
372
if (status != StatusCode::OK) {
343
373
return status;
@@ -350,7 +380,7 @@ Status convertStringValToTensor(const tensorflow::TensorProto& src, ov::Tensor&
350
380
return status;
351
381
}
352
382
353
- tensor = convertMatsToTensor (images, tensorInfo, isPipeline );
383
+ tensor = convertMatsToTensor (images, tensorInfo);
354
384
if (!tensor) {
355
385
return StatusCode::IMAGE_PARSING_FAILED;
356
386
}
0 commit comments