Skip to content

Commit

Permalink
Test refactor (#91)
Browse files Browse the repository at this point in the history
* Division by 0 in in CropMirrorNormalizeTest was fixed.
Elimination of duplicated code from WriteImage functions.

Signed-off-by: Andrei <andreii@nvidia.com>

* Fix of problem in bresnet50 benchmark

Signed-off-by: Andrei <andreii@nvidia.com>

* CropMirrorNormalizePermuteTest refactored

Signed-off-by: Andrei <andreii@nvidia.com>

* Lint problem fixed

Signed-off-by: Andrei <andreii@nvidia.com>

* JpegDecodeTest re-implemented

Signed-off-by: Andrei <andreii@nvidia.com>

* Nondefault bound for average deviation implemented for 2 decode tests

Signed-off-by: Andrei <andreii@nvidia.com>

* Executore test refactored.
The JPEG/PNG files are loaded and decoded only when it's is really
need to be done for performing of current test.

Signed-off-by: Andrei <andreii@nvidia.com>

* Lint problem fixed
Output of the images in benchmark eliminate

Signed-off-by: Andrei <andreii@nvidia.com>

* Eps for ExecutorTest and some changes, suggested by Cliff

Signed-off-by: Andrei <andreii@nvidia.com>

* Changes requested by Cliff.
- Destructor for ImageSetDescr
- Conditional compilation of WriteHWCBatch for resnet50 benchmark

Signed-off-by: Andrei <andreii@nvidia.com>

* Color augmentations on CPU

Signed-off-by: Andrei <andreii@nvidia.com>

* GenericMatchingTest class implemented
This class was used for
 - new tests for Displacement Operators.
 - refactoring of the tests for Color Operators

Signed-off-by: Andrei <andreii@nvidia.com>

* Average deviation of pixel colors for unit tests rescaled
(normalized to color range and converted to percentages)

Signed-off-by: Andrei <andreii@nvidia.com>

* Changes requested by Przemek

Signed-off-by: Andrei <andreii@nvidia.com>
  • Loading branch information
drivanov authored and ptrendx committed Aug 17, 2018
1 parent b5dd3e3 commit fb1d481
Show file tree
Hide file tree
Showing 29 changed files with 927 additions and 940 deletions.
19 changes: 7 additions & 12 deletions dali/benchmark/dali_bench.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,10 @@ class DALIBenchmark : public benchmark::Fixture {
public:
DALIBenchmark() {
rand_gen_.seed(time(nullptr));
LoadJPEGS(image_folder, &jpeg_names_, &jpegs_, &jpeg_sizes_);
LoadJPEGS(image_folder, &jpeg_names_, &jpegs_);
}

virtual ~DALIBenchmark() {
for (auto &ptr : jpegs_) {
delete[] ptr;
}
}
virtual ~DALIBenchmark() = default;

int RandInt(int a, int b) {
return std::uniform_int_distribution<>(a, b)(rand_gen_);
Expand All @@ -52,27 +48,26 @@ class DALIBenchmark : public benchmark::Fixture {
}

inline void MakeJPEGBatch(TensorList<CPUBackend> *tl, int n) {
DALI_ENFORCE(jpegs_.size() > 0, "jpegs must be loaded to create batches");
const auto nImgs = jpegs_.nImages();
DALI_ENFORCE(nImgs > 0, "jpegs must be loaded to create batches");
vector<Dims> shape(n);
for (int i = 0; i < n; ++i) {
shape[i] = {jpeg_sizes_[i % jpegs_.size()]};
shape[i] = {jpegs_.sizes_[i % nImgs]};
}

tl->template mutable_data<uint8>();
tl->Resize(shape);

for (int i = 0; i < n; ++i) {
std::memcpy(tl->template mutable_tensor<uint8>(i),
jpegs_[i % jpegs_.size()],
jpeg_sizes_[i % jpegs_.size()]);
jpegs_.data_[i % nImgs], jpegs_.sizes_[i % nImgs]);
}
}

protected:
std::mt19937 rand_gen_;
vector<string> jpeg_names_;
vector<uint8*> jpegs_;
vector<int> jpeg_sizes_;
ImgSetDescr jpegs_;
};

} // namespace dali
Expand Down
4 changes: 3 additions & 1 deletion dali/benchmark/resnet50_nvjpeg_bench.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ BENCHMARK_DEFINE_F(RealRN50, nvjpegPipe)(benchmark::State& st) { // NOLINT
}
}

WriteHWCBatch<uint8_t>(*ws.Output<GPUBackend>(0), 0, 1, "img");
#if DALI_DEBUG
WriteHWCBatch(*ws.Output<GPUBackend>(0), "img");
#endif
int num_batches = st.iterations() + static_cast<int>(pipelined);
st.counters["FPS"] = benchmark::Counter(batch_size*num_batches,
benchmark::Counter::kIsRate);
Expand Down
120 changes: 3 additions & 117 deletions dali/image/jpeg_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,136 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include <gtest/gtest.h>
#include <opencv2/opencv.hpp>

#include <cmath>
#include <fstream>
#include <stdexcept>
#include <vector>
#include <string>

#include "dali/common.h"
#include "dali/test/dali_test.h"
#include "dali/image/jpeg.h"
#include "dali/test/dali_test_decoder.h"

namespace dali {

namespace {
// Our turbo jpeg decoder cannot handle CMYK images
// or 410 images
const vector<string> tjpg_test_images = {
image_folder + "/420.jpg",
image_folder + "/422.jpg",
image_folder + "/440.jpg",
image_folder + "/444.jpg",
image_folder + "/gray.jpg",
image_folder + "/411.jpg",
image_folder + "/411-non-multiple-4-width.jpg",
image_folder + "/420-odd-height.jpg",
image_folder + "/420-odd-width.jpg",
image_folder + "/420-odd-both.jpg",
image_folder + "/422-odd-width.jpg"
};
} // namespace

// Fixture for jpeg decode testing. Templated
// to make googletest run our tests grayscale & rgb
template <typename ImgType>
class JpegDecodeTest : public DALITest {
public:
void SetUp() {
if (IsColor(img_type_)) {
c_ = 3;
} else if (img_type_ == DALI_GRAY) {
c_ = 1;
}
rand_gen_.seed(time(nullptr));
LoadJPEGS(tjpg_test_images, &jpegs_, &jpeg_sizes_);
}

void TearDown() {
DALITest::TearDown();
}

void VerifyDecode(const uint8 *img, int h, int w, int img_id) {
// Compare w/ opencv result
cv::Mat ver;
cv::Mat jpeg = cv::Mat(1, jpeg_sizes_[img_id], CV_8UC1, jpegs_[img_id]);

ASSERT_TRUE(CheckIsJPEG(jpegs_[img_id], jpeg_sizes_[img_id]));
int flag = IsColor(img_type_) ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE;
cv::imdecode(jpeg, flag, &ver);

cv::Mat ver_img(h, w, IsColor(img_type_) ? CV_8UC3 : CV_8UC2);
if (img_type_ == DALI_RGB) {
// Convert from BGR to RGB for verification
cv::cvtColor(ver, ver_img, CV_BGR2RGB);
} else {
ver_img = ver;
}

ASSERT_EQ(h, ver_img.rows);
ASSERT_EQ(w, ver_img.cols);
vector<int> diff(h*w*c_, 0);
for (int i = 0; i < h*w*c_; ++i) {
diff[i] = abs(static_cast<int>(ver_img.ptr()[i] - img[i]));
}

// calculate the MSE
float mean, std;
MeanStdDev(diff, &mean, &std);

#ifndef NDEBUG
cout << "num: " << diff.size() << endl;
cout << "mean: " << mean << endl;
cout << "std: " << std << endl;
#endif

// Note: We allow a slight deviation from the ground truth.
// This value was picked fairly arbitrarily to let the test
// pass for libjpeg turbo
ASSERT_LT(mean, 2.f);
ASSERT_LT(std, 3.f);
}

void MeanStdDev(const vector<int> &diff, float *mean, float *std) {
// Avoid division by zero
ASSERT_NE(diff.size(), 0);

double sum = 0, var_sum = 0;
for (auto &val : diff) {
sum += val;
}
*mean = sum / diff.size();
for (auto &val : diff) {
var_sum += (val - *mean)*(val - *mean);
}
*std = sqrt(var_sum / diff.size());
}

protected:
const DALIImageType img_type_ = ImgType::type;
int c_;
class JpegDecodeTest : public GenericDecoderTest<ImgType> {
};

// Run RGB & grayscale tests
typedef ::testing::Types<RGB, BGR, Gray> Types;
TYPED_TEST_CASE(JpegDecodeTest, Types);

TYPED_TEST(JpegDecodeTest, DecodeJPEGHost) {
vector<uint8> image;
for (size_t img = 0; img < this->jpegs_.size(); ++img) {
Tensor<CPUBackend> t;
DALI_CALL(DecodeJPEGHost(this->jpegs_[img],
this->jpeg_sizes_[img],
this->img_type_, &t));
#ifndef NDEBUG
cout << img << " " << tjpg_test_images[img] << " " << this->jpeg_sizes_[img] << endl;
cout << "dims: " << t.dim(1) << "x" << t.dim(0) << endl;
#endif
this->VerifyDecode(t.data<uint8_t>(), t.dim(0), t.dim(1), img);
}
this->RunTestDecode(this->jpegs_, 1.5);
}

} // namespace dali
44 changes: 44 additions & 0 deletions dali/image/transform.cc
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,48 @@ DALIError_t FastResizeCropMirrorHost(const uint8 *img, int H, int W, int C,
return DALISuccess;
}

void CheckParam(const Tensor<CPUBackend> &input, const std::string &opName) {
DALI_ENFORCE(input.ndim() == 3);
DALI_ENFORCE(IsType<uint8>(input.type()),
opName + " expects input data in uint8.");
DALI_ENFORCE(input.dim(2) == 1 || input.dim(2) == 3,
opName + " supports hwc rgb & grayscale inputs.");
}

typedef cv::Vec<uchar, 1> Vec1b;

DALIError_t MakeColorTransformation(const uint8 *img, int H, int W, int C,
const float *matr, uint8 *out_img) {
const int channel_flag = C == 3 ? CV_8UC3 : CV_8UC1;

const cv::Mat cv_imgIn = CreateMatFromPtr(H, W, channel_flag, img);
cv::Mat cv_imgOut = CreateMatFromPtr(H, W, channel_flag, out_img);

if (C == 1) {
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
cv_imgOut.at<Vec1b>(y, x)[0] =
cv::saturate_cast<uint8>((matr[0] * cv_imgIn.at<Vec1b>(y, x)[0]) + matr[1]);
}
}
} else {
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
// Using direct calculation because they are 25% faster
// than two loops which could be used here
const auto &inpPix = cv_imgIn.at<cv::Vec3b>(y, x);
auto &outPix = cv_imgOut.at<cv::Vec3b>(y, x);
outPix[0] = cv::saturate_cast<uint8>
(inpPix[0] * matr[0] + inpPix[1] * matr[1] + inpPix[2] * matr[2] + matr[3]);
outPix[1] = cv::saturate_cast<uint8>
(inpPix[0] * matr[4] + inpPix[1] * matr[5] + inpPix[2] * matr[6] + matr[7]);
outPix[2] = cv::saturate_cast<uint8>
(inpPix[0] * matr[8] + inpPix[1] * matr[9] + inpPix[2] * matr[10] + matr[11]);
}
}
}

return DALISuccess;
}

} // namespace dali
9 changes: 8 additions & 1 deletion dali/image/transform.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
#ifndef DALI_IMAGE_TRANSFORM_H_
#define DALI_IMAGE_TRANSFORM_H_

#include <string>
#include "dali/common.h"
#include "dali/error_handling.h"
#include "dali/pipeline/data/tensor.h"

namespace dali {

Expand All @@ -31,7 +33,7 @@ namespace dali {
* this temporary workspace pointer to avoid extra memory allocation. The size
* of the memory pointed to by 'workspace' should be rsz_h*rsz_w*C bytes
*
* Note: We leave the calculate of the resize dimesions & the decision of whether
* Note: We leave the calculate of the resize dimensions & the decision of whether
* to mirror the image or not external to the function. With the GPU version of
* this function, these params will need to have been calculated before-hand
* and, in the case of a batched call, copied to the device. Separating these
Expand Down Expand Up @@ -65,6 +67,11 @@ DALIError_t FastResizeCropMirrorHost(const uint8 *img, int H, int W, int C,
int mirror, uint8 *out_img, DALIInterpType type = DALI_INTERP_LINEAR,
uint8 *workspace = nullptr);

void CheckParam(const Tensor<CPUBackend> &input, const std::string &pOperator);

DALIError_t MakeColorTransformation(const uint8 *img, int H, int W, int C,
const float *matrix, uint8 *out_img);

} // namespace dali

#endif // DALI_IMAGE_TRANSFORM_H_
4 changes: 2 additions & 2 deletions dali/pipeline/data/allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ DALI_DECLARE_OPTYPE_REGISTRY(GPUAllocator, GPUAllocator);

#define DALI_REGISTER_GPU_ALLOCATOR(OpName, OpType) \
DALI_DEFINE_OPTYPE_REGISTERER(OpName, OpType, \
dali::GPUAllocator, dali::GPUAllocator)
dali::GPUAllocator, dali::GPUAllocator, "GPU_Allocator")


/**
Expand All @@ -99,7 +99,7 @@ DALI_DECLARE_OPTYPE_REGISTRY(CPUAllocator, CPUAllocator);

#define DALI_REGISTER_CPU_ALLOCATOR(OpName, OpType) \
DALI_DEFINE_OPTYPE_REGISTERER(OpName, OpType, \
dali::CPUAllocator, dali::CPUAllocator)
dali::CPUAllocator, dali::CPUAllocator, "CPU_Allocator")

/**
* @brief Pinned memory CPU allocator
Expand Down
Loading

0 comments on commit fb1d481

Please sign in to comment.