From 3b01eaa3d89249f675dba1494a373682e821d78c Mon Sep 17 00:00:00 2001 From: Aleksandr Rybnikov Date: Wed, 10 May 2017 18:02:44 +0300 Subject: [PATCH] Fixed bugs after dnn refactoring. Added new tests for layers (#1142) * Fixed bugs after dnn refactoring. Added new tests for layers * Fixed torch tests --- modules/dnn/src/dnn.cpp | 2 +- modules/dnn/src/layers/op_im2col.cpp | 18 +- modules/dnn/src/torch/THDiskFile.cpp | 199 ++++++++++++++++++++++- modules/dnn/src/torch/THDiskFile.h | 2 + modules/dnn/src/torch/THFile.cpp | 6 +- modules/dnn/src/torch/THFile.h | 9 +- modules/dnn/src/torch/THFilePrivate.h | 4 +- modules/dnn/src/torch/torch_importer.cpp | 8 +- modules/dnn/test/test_common.hpp | 7 +- modules/dnn/test/test_googlenet.cpp | 2 +- modules/dnn/test/test_layers.cpp | 15 ++ modules/dnn/test/test_torch_importer.cpp | 5 +- 12 files changed, 242 insertions(+), 35 deletions(-) diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index 5b6fdb1ef..a2a3e1224 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -607,7 +607,7 @@ void Net::setBlob(String outputName, const Mat &blob_) MatSize prevShape = ld.outputBlobs[pin.oid].size; ld.outputBlobs[pin.oid] = blob_.clone(); - impl->netWasAllocated = prevShape == blob_.size; + impl->netWasAllocated = impl->netWasAllocated && prevShape == blob_.size; } Mat Net::getBlob(String outputName) diff --git a/modules/dnn/src/layers/op_im2col.cpp b/modules/dnn/src/layers/op_im2col.cpp index e9a832e68..b29dac49f 100644 --- a/modules/dnn/src/layers/op_im2col.cpp +++ b/modules/dnn/src/layers/op_im2col.cpp @@ -153,12 +153,7 @@ public: t.width_col = width_col; t.channels_col = channels * kernel_h * kernel_w; - int total = t.height_col*t.width_col; -#if 1 - t(Range(0, total)); -#else - cv::parallel_for_(Range(0, total), t, 16); -#endif + cv::parallel_for_(Range(0, t.height_col*t.width_col), t, 16); } virtual void operator ()(const Range &r) const @@ -203,7 +198,6 @@ public: } else { - memset(data_col_, 0, kw*kh*channels*sizeof(data_col_[0])); for(int i_c = 0; i_c < channels; i_c++) { int channels_offset = i_c * width * height; @@ -242,7 +236,6 @@ void im2row(const float* data_im, int channels, int height, int width, } -#if 0 template class col2im_CpuPBody : public cv::ParallelLoopBody { @@ -312,7 +305,6 @@ public: } } }; -#endif //single-threaded version template @@ -360,9 +352,15 @@ void col2im(const float* data_col, int channels, int height, int width, int stride_h, int stride_w, int dilation_h, int dilation_w, float* data_im, const int* ofsbuf) { - //col2im_CpuPBody::run(data_col, channels, height, width, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, data_im); + (void)dilation_h; + (void)dilation_w; + (void)ofsbuf; + col2im_CpuPBody::run(data_col, channels, height, width, kernel_h, + kernel_w, pad_h, pad_w, stride_h, stride_w, data_im); +#if 0 col2im_cpu(data_col, channels, height, width, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, data_im, ofsbuf); +#endif } } diff --git a/modules/dnn/src/torch/THDiskFile.cpp b/modules/dnn/src/torch/THDiskFile.cpp index d123c1047..c56b0f489 100644 --- a/modules/dnn/src/torch/THDiskFile.cpp +++ b/modules/dnn/src/torch/THDiskFile.cpp @@ -13,6 +13,7 @@ typedef struct THDiskFile__ FILE *handle; char *name; int isNativeEncoding; + int longSize; } THDiskFile; @@ -172,9 +173,17 @@ static void THDiskFile_seek(THFile *self, long position) THDiskFile *dfself = (THDiskFile*)(self); THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file"); - THArgCheck(position >= 0, 2, "position must be positive"); - if(fseek(dfself->handle, position, SEEK_SET) < 0) +#if defined(_WIN64) + THArgCheck(position <= (_int64)INT64_MAX, 2, "position must be smaller than INT64_MAX"); + if(_fseeki64(dfself->handle, (__int64)position, SEEK_SET) < 0) +#elif defined(_WIN32) + THArgCheck(position <= (long)LONG_MAX, 2, "position must be smaller than LONG_MAX"); + if(fseek(dfself->handle, (long)position, SEEK_SET) < 0) +#else + THArgCheck(position <= (long)LLONG_MAX, 2, "position must be smaller than LLONG_MAX"); + if(fseeko(dfself->handle, (off_t)position, SEEK_SET) < 0) +#endif { dfself->file.hasError = 1; if(!dfself->file.isQuiet) @@ -188,7 +197,13 @@ static void THDiskFile_seekEnd(THFile *self) THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file"); +#if defined(_WIN64) + if(_fseeki64(dfself->handle, 0L, SEEK_END) < 0) +#elif defined(_WIN32) if(fseek(dfself->handle, 0L, SEEK_END) < 0) +#else + if(fseeko(dfself->handle, 0L, SEEK_END) < 0) +#endif { dfself->file.hasError = 1; if(!dfself->file.isQuiet) @@ -200,7 +215,20 @@ static long THDiskFile_position(THFile *self) { THDiskFile *dfself = (THDiskFile*)(self); THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file"); - return ftell(dfself->handle); + +#if defined(_WIN64) + __int64 offset = _ftelli64(dfself->handle); +#elif defined(_WIN32) + long offset = ftell(dfself->handle); +#else + off_t offset = ftello(dfself->handle); +#endif + if (offset > -1) + return (long)offset; + else if(!dfself->file.isQuiet) + THError("unable to obtain disk file offset (maybe a long overflow occurred)"); + + return 0; } static void THDiskFile_close(THFile *self) @@ -274,6 +302,23 @@ void THDiskFile_bigEndianEncoding(THFile *self) /* End of Little and Big Endian Stuff */ +void THDiskFile_longSize(THFile *self, int size) +{ + THDiskFile *dfself = (THDiskFile*)(self); + THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file"); + THArgCheck(size == 0 || size == 4 || size == 8, 1, "Invalid long size specified"); + dfself->longSize = size; +} + +void THDiskFile_noBuffer(THFile *self) +{ + THDiskFile *dfself = (THDiskFile*)(self); + THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file"); + if (setvbuf(dfself->handle, NULL, _IONBF, 0)) { + THError("error: cannot disable buffer"); + } +} + static void THDiskFile_free(THFile *self) { THDiskFile *dfself = (THDiskFile*)(self); @@ -302,12 +347,12 @@ READ_WRITE_METHODS(short, Short, int ret = fprintf(dfself->handle, "%hd", data[i]); if(ret <= 0) break; else nwrite++) READ_WRITE_METHODS(int, Int, - int ret = fscanf(dfself->handle, "%d", &data[i]); if(ret <= 0) break; else nread++, + int ret = fscanf(dfself->handle, "%d\n\r", &data[i]); if(ret <= 0) break; else nread++, int ret = fprintf(dfself->handle, "%d", data[i]); if(ret <= 0) break; else nwrite++) -READ_WRITE_METHODS(long, Long, +/*READ_WRITE_METHODS(long, Long, int ret = fscanf(dfself->handle, "%ld", &data[i]); if(ret <= 0) break; else nread++, - int ret = fprintf(dfself->handle, "%ld", data[i]); if(ret <= 0) break; else nwrite++) + int ret = fprintf(dfself->handle, "%ld", data[i]); if(ret <= 0) break; else nwrite++)*/ READ_WRITE_METHODS(float, Float, int ret = fscanf(dfself->handle, "%g", &data[i]); if(ret <= 0) break; else nread++, @@ -317,6 +362,146 @@ READ_WRITE_METHODS(double, Double, int ret = fscanf(dfself->handle, "%lg", &data[i]); if(ret <= 0) break; else nread++, int ret = fprintf(dfself->handle, "%.17g", data[i]); if(ret <= 0) break; else nwrite++) + +/* For Long we need to rewrite everything, because of the special management of longSize */ +static long THDiskFile_readLong(THFile *self, int64 *data, long n) +{ + THDiskFile *dfself = (THDiskFile*)(self); + long nread = 0L; + + THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file"); + THArgCheck(dfself->file.isReadable, 1, "attempt to read in a write-only file"); + + if(dfself->file.isBinary) + { + if(dfself->longSize == 0 || dfself->longSize == sizeof(int64)) + { + nread = fread__(data, sizeof(int64), n, dfself->handle); + if(!dfself->isNativeEncoding && (sizeof(int64) > 1) && (nread > 0)) + THDiskFile_reverseMemory(data, data, sizeof(int64), nread); + } else if(dfself->longSize == 4) + { + nread = fread__(data, 4, n, dfself->handle); + if(!dfself->isNativeEncoding && (nread > 0)) + THDiskFile_reverseMemory(data, data, 4, nread); + long i; + for(i = nread; i > 0; i--) + data[i-1] = ((int *)data)[i-1]; + } + else /* if(dfself->longSize == 8) */ + { + int big_endian = !THDiskFile_isLittleEndianCPU(); + int32_t *buffer = (int32_t*)THAlloc(8*n); + nread = fread__(buffer, 8, n, dfself->handle); + long i; + for(i = nread; i > 0; i--) + data[i-1] = buffer[2*(i-1) + big_endian]; + THFree(buffer); + if(!dfself->isNativeEncoding && (nread > 0)) + THDiskFile_reverseMemory(data, data, 4, nread); + } + } + else + { + long i; + for(i = 0; i < n; i++) + { + long d; + int ret = fscanf(dfself->handle, "%ld", &d); if(ret <= 0) break; else nread++; + data[i] = d; + } + if(dfself->file.isAutoSpacing && (n > 0)) + { + int c = fgetc(dfself->handle); + if( (c != '\n') && (c != EOF) ) + ungetc(c, dfself->handle); + } + } + + if(nread != n) + { + dfself->file.hasError = 1; /* shouldn't we put hasError to 0 all the time ? */ + if(!dfself->file.isQuiet) + THError("read error: read %d blocks instead of %d", nread, n); + } + + return nread; +} + +static long THDiskFile_writeLong(THFile *self, int64 *data, long n) +{ + THDiskFile *dfself = (THDiskFile*)(self); + long nwrite = 0L; + + THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file"); + THArgCheck(dfself->file.isWritable, 1, "attempt to write in a read-only file"); + + if(dfself->file.isBinary) + { + if(dfself->longSize == 0 || dfself->longSize == sizeof(long)) + { + if(dfself->isNativeEncoding) + { + nwrite = fwrite(data, sizeof(long), n, dfself->handle); + } + else + { + char *buffer = (char*)THAlloc(sizeof(long)*n); + THDiskFile_reverseMemory(buffer, data, sizeof(long), n); + nwrite = fwrite(buffer, sizeof(long), n, dfself->handle); + THFree(buffer); + } + } else if(dfself->longSize == 4) + { + int32_t *buffer = (int32_t *)THAlloc(4*n); + long i; + for(i = 0; i < n; i++) + buffer[i] = data[i]; + if(!dfself->isNativeEncoding) + THDiskFile_reverseMemory(buffer, buffer, 4, n); + nwrite = fwrite(buffer, 4, n, dfself->handle); + THFree(buffer); + } + else /* if(dfself->longSize == 8) */ + { + int big_endian = !THDiskFile_isLittleEndianCPU(); + int32_t *buffer = (int32_t*)THAlloc(8*n); + long i; + for(i = 0; i < n; i++) + { + buffer[2*i + !big_endian] = 0; + buffer[2*i + big_endian] = data[i]; + } + if(!dfself->isNativeEncoding) + THDiskFile_reverseMemory(buffer, buffer, 8, n); + nwrite = fwrite(buffer, 8, n, dfself->handle); + THFree(buffer); + } + } + else + { + long i; + for(i = 0; i < n; i++) + { + long res = 0; + int ret = fprintf(dfself->handle, "%ld", res); data[i] = res; if(ret <= 0) break; else nwrite++; + if( dfself->file.isAutoSpacing && (i < n-1) ) + fprintf(dfself->handle, " "); + } + if(dfself->file.isAutoSpacing && (n > 0)) + fprintf(dfself->handle, "\n"); + } + + if(nwrite != n) + { + dfself->file.hasError = 1; + if(!dfself->file.isQuiet) + THError("write error: wrote %d blocks instead of %d", nwrite, n); + } + + return nwrite; +} + static long THDiskFile_readString(THFile *self, const char *format, char **str_) { THDiskFile *dfself = (THDiskFile*)(self); @@ -494,6 +679,7 @@ THFile *THDiskFile_new(const char *name, const char *mode, int isQuiet) self->name = (char*)THAlloc(strlen(name)+1); strcpy(self->name, name); self->isNativeEncoding = 1; + self->longSize = 0; self->file.vtable = &vtable; self->file.isQuiet = isQuiet; @@ -595,6 +781,7 @@ THFile *THPipeFile_new(const char *name, const char *mode, int isQuiet) self->name = (char*)THAlloc(strlen(name)+1); strcpy(self->name, name); self->isNativeEncoding = 1; + self->longSize = 0; self->file.vtable = &vtable; self->file.isQuiet = isQuiet; diff --git a/modules/dnn/src/torch/THDiskFile.h b/modules/dnn/src/torch/THDiskFile.h index f7c93c220..bc5c001c7 100644 --- a/modules/dnn/src/torch/THDiskFile.h +++ b/modules/dnn/src/torch/THDiskFile.h @@ -13,5 +13,7 @@ TH_API int THDiskFile_isBigEndianCPU(void); TH_API void THDiskFile_nativeEndianEncoding(THFile *self); TH_API void THDiskFile_littleEndianEncoding(THFile *self); TH_API void THDiskFile_bigEndianEncoding(THFile *self); +TH_API void THDiskFile_longSize(THFile *self, int size); +TH_API void THDiskFile_noBuffer(THFile *self); #endif diff --git a/modules/dnn/src/torch/THFile.cpp b/modules/dnn/src/torch/THFile.cpp index 148f17915..9e055105c 100644 --- a/modules/dnn/src/torch/THFile.cpp +++ b/modules/dnn/src/torch/THFile.cpp @@ -1,7 +1,7 @@ #if defined(ENABLE_TORCH_IMPORTER) && ENABLE_TORCH_IMPORTER #include "THFile.h" #include "THFilePrivate.h" - +#include extern "C" { @@ -20,7 +20,7 @@ IMPLEMENT_THFILE_RW(Byte, unsigned char) IMPLEMENT_THFILE_RW(Char, char) IMPLEMENT_THFILE_RW(Short, short) IMPLEMENT_THFILE_RW(Int, int) -IMPLEMENT_THFILE_RW(Long, long) +IMPLEMENT_THFILE_RW(Long, int64) IMPLEMENT_THFILE_RW(Float, float) IMPLEMENT_THFILE_RW(Double, double) @@ -134,7 +134,7 @@ IMPLEMENT_THFILE_SCALAR(Byte, unsigned char) IMPLEMENT_THFILE_SCALAR(Char, char) IMPLEMENT_THFILE_SCALAR(Short, short) IMPLEMENT_THFILE_SCALAR(Int, int) -IMPLEMENT_THFILE_SCALAR(Long, long) +IMPLEMENT_THFILE_SCALAR(Long, int64) IMPLEMENT_THFILE_SCALAR(Float, float) IMPLEMENT_THFILE_SCALAR(Double, double) diff --git a/modules/dnn/src/torch/THFile.h b/modules/dnn/src/torch/THFile.h index 32100e143..de362150f 100644 --- a/modules/dnn/src/torch/THFile.h +++ b/modules/dnn/src/torch/THFile.h @@ -4,6 +4,7 @@ //#include "THStorage.h" #if defined(ENABLE_TORCH_IMPORTER) && ENABLE_TORCH_IMPORTER #include "THGeneral.h" +#include "opencv2/core/hal/interface.h" typedef struct THFile__ THFile; @@ -28,7 +29,7 @@ TH_API unsigned char THFile_readByteScalar(THFile *self); TH_API char THFile_readCharScalar(THFile *self); TH_API short THFile_readShortScalar(THFile *self); TH_API int THFile_readIntScalar(THFile *self); -TH_API long THFile_readLongScalar(THFile *self); +TH_API int64 THFile_readLongScalar(THFile *self); TH_API float THFile_readFloatScalar(THFile *self); TH_API double THFile_readDoubleScalar(THFile *self); @@ -36,7 +37,7 @@ TH_API void THFile_writeByteScalar(THFile *self, unsigned char scalar); TH_API void THFile_writeCharScalar(THFile *self, char scalar); TH_API void THFile_writeShortScalar(THFile *self, short scalar); TH_API void THFile_writeIntScalar(THFile *self, int scalar); -TH_API void THFile_writeLongScalar(THFile *self, long scalar); +TH_API void THFile_writeLongScalar(THFile *self, int64 scalar); TH_API void THFile_writeFloatScalar(THFile *self, float scalar); TH_API void THFile_writeDoubleScalar(THFile *self, double scalar); @@ -64,7 +65,7 @@ TH_API long THFile_readByteRaw(THFile *self, unsigned char *data, long n); TH_API long THFile_readCharRaw(THFile *self, char *data, long n); TH_API long THFile_readShortRaw(THFile *self, short *data, long n); TH_API long THFile_readIntRaw(THFile *self, int *data, long n); -TH_API long THFile_readLongRaw(THFile *self, long *data, long n); +TH_API long THFile_readLongRaw(THFile *self, int64 *data, long n); TH_API long THFile_readFloatRaw(THFile *self, float *data, long n); TH_API long THFile_readDoubleRaw(THFile *self, double *data, long n); TH_API long THFile_readStringRaw(THFile *self, const char *format, char **str_); /* you must deallocate str_ */ @@ -73,7 +74,7 @@ TH_API long THFile_writeByteRaw(THFile *self, unsigned char *data, long n); TH_API long THFile_writeCharRaw(THFile *self, char *data, long n); TH_API long THFile_writeShortRaw(THFile *self, short *data, long n); TH_API long THFile_writeIntRaw(THFile *self, int *data, long n); -TH_API long THFile_writeLongRaw(THFile *self, long *data, long n); +TH_API long THFile_writeLongRaw(THFile *self, int64 *data, long n); TH_API long THFile_writeFloatRaw(THFile *self, float *data, long n); TH_API long THFile_writeDoubleRaw(THFile *self, double *data, long n); TH_API long THFile_writeStringRaw(THFile *self, const char *str, long size); diff --git a/modules/dnn/src/torch/THFilePrivate.h b/modules/dnn/src/torch/THFilePrivate.h index 9097fb979..dbef9f745 100644 --- a/modules/dnn/src/torch/THFilePrivate.h +++ b/modules/dnn/src/torch/THFilePrivate.h @@ -20,7 +20,7 @@ struct THFileVTable long (*readChar)(THFile *self, char *data, long n); long (*readShort)(THFile *self, short *data, long n); long (*readInt)(THFile *self, int *data, long n); - long (*readLong)(THFile *self, long *data, long n); + long (*readLong)(THFile *self, int64 *data, long n); long (*readFloat)(THFile *self, float *data, long n); long (*readDouble)(THFile *self, double *data, long n); long (*readString)(THFile *self, const char *format, char **str_); @@ -29,7 +29,7 @@ struct THFileVTable long (*writeChar)(THFile *self, char *data, long n); long (*writeShort)(THFile *self, short *data, long n); long (*writeInt)(THFile *self, int *data, long n); - long (*writeLong)(THFile *self, long *data, long n); + long (*writeLong)(THFile *self, int64 *data, long n); long (*writeFloat)(THFile *self, float *data, long n); long (*writeDouble)(THFile *self, double *data, long n); long (*writeString)(THFile *self, const char *str, long size); diff --git a/modules/dnn/src/torch/torch_importer.cpp b/modules/dnn/src/torch/torch_importer.cpp index 61b6ec5a3..803c80bd0 100644 --- a/modules/dnn/src/torch/torch_importer.cpp +++ b/modules/dnn/src/torch/torch_importer.cpp @@ -253,10 +253,10 @@ struct TorchImporter : public ::cv::dnn::Importer case CV_USRTYPE1: { double *buf = storageMat.ptr(); - THFile_readLongRaw(file, (long*)buf, size); + THFile_readLongRaw(file, (int64*)buf, size); for (size_t i = (size_t)size; i-- > 0; ) - buf[i] = ((long*)buf)[i]; + buf[i] = ((int64*)buf)[i]; } break; default: @@ -352,8 +352,8 @@ struct TorchImporter : public ::cv::dnn::Importer void readTorchTensor(int indexTensor, int typeTensor) { int ndims = readInt(); - AutoBuffer sizes(ndims); - AutoBuffer steps(ndims); + AutoBuffer sizes(ndims); + AutoBuffer steps(ndims); THFile_readLongRaw(file, sizes, ndims); THFile_readLongRaw(file, steps, ndims); long offset = readLong() - 1; diff --git a/modules/dnn/test/test_common.hpp b/modules/dnn/test/test_common.hpp index eb38d766c..a436daece 100644 --- a/modules/dnn/test/test_common.hpp +++ b/modules/dnn/test/test_common.hpp @@ -47,13 +47,14 @@ inline const std::string &getOpenCVExtraDir() return cvtest::TS::ptr()->get_data_path(); } -inline void normAssert(cv::InputArray ref, cv::InputArray test, const char *comment = "") +inline void normAssert(cv::InputArray ref, cv::InputArray test, const char *comment = "", + double l1 = 0.00001, double lInf = 0.0001) { double normL1 = cvtest::norm(ref, test, cv::NORM_L1) / ref.getMat().total(); - EXPECT_LE(normL1, 0.002) << comment; + EXPECT_LE(normL1, l1) << comment; double normInf = cvtest::norm(ref, test, cv::NORM_INF); - EXPECT_LE(normInf, 0.08) << comment; + EXPECT_LE(normInf, lInf) << comment; } #endif diff --git a/modules/dnn/test/test_googlenet.cpp b/modules/dnn/test/test_googlenet.cpp index 82f3ec1a8..4bf1429d4 100644 --- a/modules/dnn/test/test_googlenet.cpp +++ b/modules/dnn/test/test_googlenet.cpp @@ -72,7 +72,7 @@ static void launchGoogleNetTest() inpMats.push_back( imread(_tf("googlenet_1.jpg")) ); ASSERT_TRUE(!inpMats[0].empty() && !inpMats[1].empty()); - net.setBlob(".data", blobFromImages(inpMats, 1.)); + net.setBlob(".data", blobFromImages(inpMats, 1., false)); net.forward(); Mat out = net.getBlob("prob"); diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 34a8ef243..5ddc2533c 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -184,6 +184,21 @@ TEST(Layer_Test_BatchNorm, Accuracy) testLayerUsingCaffeModels("layer_batch_norm", true); } +TEST(Layer_Test_ReLU, Accuracy) +{ + testLayerUsingCaffeModels("layer_relu"); +} + +TEST(Layer_Test_Dropout, Accuracy) +{ + testLayerUsingCaffeModels("layer_dropout"); +} + +TEST(Layer_Test_Concat, Accuracy) +{ + testLayerUsingCaffeModels("layer_concat"); +} + //template //static void test_Layer_Concat() //{ diff --git a/modules/dnn/test/test_torch_importer.cpp b/modules/dnn/test/test_torch_importer.cpp index 3da0ccac7..a9f122e5c 100644 --- a/modules/dnn/test/test_torch_importer.cpp +++ b/modules/dnn/test/test_torch_importer.cpp @@ -175,7 +175,10 @@ TEST(Torch_Importer, ENet_accuracy) net.forward(); Mat out = net.getBlob(net.getLayerNames().back()); Mat ref = blobFromNPY(_tf("torch_enet_prob.npy", false)); - normAssert(ref, out); + // Due to numerical instability in Pooling-Unpooling layers (indexes jittering) + // thresholds for ENet must be changed. Accuracy of resuults was checked on + // Cityscapes dataset and difference in mIOU with Torch is 10E-4% + normAssert(ref, out, "", 0.00044, 0.44); } }