fix random seed behaviour

pull/510/head
Alexey Shevlyakov 5 years ago
parent e4f4b9f4cd
commit 6acae622dc

@ -24,7 +24,6 @@
#endif
#include "dataset/kernels/image/cut_out_op.h"
#include "dataset/kernels/image/decode_op.h"
#include "dataset/kernels/image/distort_bounding_box_crop_op.h"
#include "dataset/kernels/image/hwc_to_chw_op.h"
#include "dataset/kernels/image/image_utils.h"
#include "dataset/kernels/image/normalize_op.h"
@ -369,18 +368,6 @@ void bindTensorOps3(py::module *m) {
}
void bindTensorOps4(py::module *m) {
(void)py::class_<DistortBoundingBoxCropOp, TensorOp, std::shared_ptr<DistortBoundingBoxCropOp>>(
*m, "DistortBoundingBoxCropOp",
"Tensor operator to crop an image randomly as long as the cropped image has sufficient "
"overlap with any one bounding box associated with original image"
"Takes aspect ratio of the generated crop box, the intersection ratio of crop box and bounding box,"
"crop ratio lower and upper bounds"
"Optional parameters: number of attempts for crop, number of attempts of crop box generation")
.def(py::init<float, float, float, float, int32_t, int32_t>(), py::arg("aspect_ratio"), py::arg("intersect_ratio"),
py::arg("crop_ratio_lower_bound"), py::arg("crop_ratio_upper_bound"),
py::arg("max_attempts") = DistortBoundingBoxCropOp::kDefMaxAttempts,
py::arg("box_gen_attempts") = DistortBoundingBoxCropOp::kDefBoxGenAttempts);
(void)py::class_<TypeCastOp, TensorOp, std::shared_ptr<TypeCastOp>>(
*m, "TypeCastOp", "Tensor operator to type cast data to a specified type.")
.def(py::init<DataType>(), py::arg("data_type"))

@ -3,7 +3,6 @@ if (WIN32)
center_crop_op.cc
cut_out_op.cc
decode_op.cc
distort_bounding_box_crop_op.cc
hwc_to_chw_op.cc
image_utils.cc
normalize_op.cc
@ -27,7 +26,6 @@ else()
change_mode_op.cc
cut_out_op.cc
decode_op.cc
distort_bounding_box_crop_op.cc
hwc_to_chw_op.cc
image_utils.cc
normalize_op.cc

@ -33,7 +33,8 @@ const uint8_t CutOutOp::kDefFillB = 0;
// constructor
CutOutOp::CutOutOp(int32_t box_height, int32_t box_width, int32_t num_patches, bool random_color, uint8_t fill_r,
uint8_t fill_g, uint8_t fill_b)
: box_height_(box_height),
: rnd_(GetSeed()),
box_height_(box_height),
box_width_(box_width),
num_patches_(num_patches),
random_color_(random_color),
@ -46,8 +47,8 @@ Status CutOutOp::Compute(const std::shared_ptr<Tensor> &input, std::shared_ptr<T
IO_CHECK(input, output);
std::shared_ptr<CVTensor> inputCV = CVTensor::AsCVTensor(input);
// cut out will clip the erasing area if the box is near the edge of the image and the boxes are black
RETURN_IF_NOT_OK(
Erase(inputCV, output, box_height_, box_width_, num_patches_, false, random_color_, fill_r_, fill_g_, fill_b_));
RETURN_IF_NOT_OK(Erase(inputCV, output, box_height_, box_width_, num_patches_, false, random_color_, &rnd_, fill_r_,
fill_g_, fill_b_));
return Status::OK();
}
} // namespace dataset

@ -62,6 +62,7 @@ class CutOutOp : public TensorOp {
Status Compute(const std::shared_ptr<Tensor> &input, std::shared_ptr<Tensor> *output) override;
private:
std::mt19937 rnd_;
int32_t box_height_;
int32_t box_width_;
int32_t num_patches_;

@ -1,117 +0,0 @@
/**
* Copyright 2019 Huawei Technologies Co., Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "dataset/kernels/image/distort_bounding_box_crop_op.h"
#include <random>
#include "dataset/core/cv_tensor.h"
#include "dataset/kernels/image/image_utils.h"
#include "dataset/util/random.h"
#include "dataset/util/status.h"
namespace mindspore {
namespace dataset {
const int32_t DistortBoundingBoxCropOp::kDefMaxAttempts = 100;
const int32_t DistortBoundingBoxCropOp::kDefBoxGenAttempts = 10;
DistortBoundingBoxCropOp::DistortBoundingBoxCropOp(float aspect_ratio, float intersect_ratio, float crop_ratio_lb,
float crop_ratio_ub, int32_t max_attempts, int32_t box_gen_attempts)
: max_attempts_(max_attempts),
box_gen_attempts_(box_gen_attempts),
aspect_ratio_(aspect_ratio),
intersect_ratio_(intersect_ratio),
crop_ratio_lb_(crop_ratio_lb),
crop_ratio_ub_(crop_ratio_ub) {
seed_ = GetSeed();
rnd_.seed(seed_);
}
Status DistortBoundingBoxCropOp::Compute(const std::vector<std::shared_ptr<Tensor>> &input,
std::vector<std::shared_ptr<Tensor>> *output) {
IO_CHECK_VECTOR(input, output);
if (input.size() != NumInput())
return Status(StatusCode::kUnexpectedError, __LINE__, __FILE__, "Number of inputs is not 5");
CHECK_FAIL_RETURN_UNEXPECTED(input[1]->shape().Size() >= 1, "The shape of the second tensor is abnormal");
int64_t num_boxes = 0;
for (uint64_t i = 1; i < input.size(); i++) {
if (i == 1) num_boxes = input[i]->shape()[0];
if (num_boxes != input[i]->shape()[0])
return Status(StatusCode::kUnexpectedError, __LINE__, __FILE__, "Numbers of boxes do not match");
if (input[i]->type() != DataType::DE_FLOAT32)
return Status(StatusCode::kUnexpectedError, __LINE__, __FILE__, "boxes' type is not DE_FLOAT21");
}
// assume input Tensor vector in the order of [img, bbox_y_min, bbox_y_max, bbox_x_min, bbox_x_max]
CHECK_FAIL_RETURN_UNEXPECTED(input[0]->shape().Size() >= 2, "The shape of the first tensor is abnormal");
int h_in = input[0]->shape()[0];
int w_in = input[0]->shape()[1];
std::vector<cv::Rect> bounding_boxes;
for (int64_t i = 0; i < num_boxes; ++i) {
// bbox coordinates are floats relative to the image width and height
float y_min, y_max, x_min, x_max;
RETURN_IF_NOT_OK(input[1]->GetItemAt<float>(&y_min, {i}));
RETURN_IF_NOT_OK(input[2]->GetItemAt<float>(&y_max, {i}));
RETURN_IF_NOT_OK(input[3]->GetItemAt<float>(&x_min, {i}));
RETURN_IF_NOT_OK(input[4]->GetItemAt<float>(&x_max, {i}));
bounding_boxes.emplace_back(static_cast<int>(x_min * w_in), static_cast<int>(y_min * h_in),
static_cast<int>((x_max - x_min) * w_in), static_cast<int>((y_max - y_min) * h_in));
}
cv::Rect output_box;
bool should_crop = false;
// go over iterations, if no satisfying box found we return the original image
for (int32_t t = 0; t < max_attempts_; ++t) {
// try to generate random box
RETURN_IF_NOT_OK(GenerateRandomCropBox(h_in, w_in, aspect_ratio_, crop_ratio_lb_, crop_ratio_ub_,
box_gen_attempts_, // int maxIter, should not be needed here
&output_box, seed_));
RETURN_IF_NOT_OK(CheckOverlapConstraint(output_box,
bounding_boxes, // have to change, should take tensor or add bbox logic
intersect_ratio_, &should_crop));
if (should_crop) {
// found a box to crop
break;
}
}
// essentially we have to check this again at the end to return original tensor
if (should_crop) {
std::shared_ptr<Tensor> out;
RETURN_IF_NOT_OK(Crop(input[0], &out, output_box.x, output_box.y, output_box.width, output_box.height));
output->push_back(out);
} else {
output->push_back(input[0]);
}
return Status::OK();
}
Status DistortBoundingBoxCropOp::OutputShape(const std::vector<TensorShape> &inputs,
std::vector<TensorShape> &outputs) {
RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs));
outputs.clear();
TensorShape out = TensorShape{-1, -1};
if (inputs[0].Rank() == 2) outputs.emplace_back(out);
if (inputs[0].Rank() == 3) outputs.emplace_back(out.AppendDim(inputs[0][2]));
if (!outputs.empty()) return Status::OK();
return Status(StatusCode::kUnexpectedError, "Input has a wrong shape");
}
Status DistortBoundingBoxCropOp::OutputType(const std::vector<DataType> &inputs, std::vector<DataType> &outputs) {
RETURN_IF_NOT_OK(TensorOp::OutputType(inputs, outputs));
outputs[0] = inputs[0];
return Status::OK();
}
} // namespace dataset
} // namespace mindspore

@ -1,72 +0,0 @@
/**
* Copyright 2019 Huawei Technologies Co., Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef DATASET_KERNELS_IMAGE_DISTORT_BOUNDING_BOX_CROP_OP_H_
#define DATASET_KERNELS_IMAGE_DISTORT_BOUNDING_BOX_CROP_OP_H_
#include <memory>
#include <random>
#include <vector>
#include "dataset/core/tensor.h"
#include "dataset/kernels/tensor_op.h"
#include "dataset/util/status.h"
namespace mindspore {
namespace dataset {
class DistortBoundingBoxCropOp : public TensorOp {
public:
// Default values, also used by python_bindings.cc
static const int32_t kDefMaxAttempts;
static const int32_t kDefBoxGenAttempts;
// Constructor for DistortBoundingBoxCropOp
// @param max_attempts tries before the crop happens
// @param box_gen_attempts crop box generation attempts
// @param aspect_ratio aspect ratio of the generated crop box
// @param intersect_ratio area overlap ratio, condition for crop only if area over lap between the generated
// crop box has sufficient overlap with any 1 bounding box
// @param crop_ratio_lb the crop ratio lower bound
// @param crop_ratio_ub the crop ratio upper bound
// @param seed
DistortBoundingBoxCropOp(float aspect_ratio, float intersect_ratio, float crop_ratio_lb, float crop_ratio_ub,
int32_t max_attempts = kDefMaxAttempts, int32_t box_gen_attempts = kDefBoxGenAttempts);
~DistortBoundingBoxCropOp() override = default;
void Print(std::ostream &out) const override {
out << "DistortBoundingBoxCropOp: " << max_attempts_ << " " << intersect_ratio_;
}
Status Compute(const std::vector<std::shared_ptr<Tensor>> &input,
std::vector<std::shared_ptr<Tensor>> *output) override;
uint32_t NumInput() override { return 5; }
Status OutputShape(const std::vector<TensorShape> &inputs, std::vector<TensorShape> &outputs) override;
Status OutputType(const std::vector<DataType> &inputs, std::vector<DataType> &outputs) override;
private:
int32_t max_attempts_;
int32_t box_gen_attempts_;
float aspect_ratio_;
float intersect_ratio_;
float crop_ratio_lb_;
float crop_ratio_ub_;
std::mt19937 rnd_;
uint32_t seed_;
};
} // namespace dataset
} // namespace mindspore
#endif // DATASET_KERNELS_IMAGE_DISTORT_BOUNDING_BOX_CROP_OP_H_

@ -636,76 +636,10 @@ Status AdjustHue(const std::shared_ptr<Tensor> &input, std::shared_ptr<Tensor> *
return Status::OK();
}
Status GenerateRandomCropBox(int input_height, int input_width, float ratio, float lb, float ub, int max_itr,
cv::Rect *crop_box, uint32_t seed) {
try {
std::mt19937 rnd;
rnd.seed(GetSeed());
if (input_height <= 0 || input_width <= 0 || ratio <= 0.0 || lb <= 0.0 || lb > ub) {
RETURN_STATUS_UNEXPECTED("Invalid inputs GenerateRandomCropBox");
}
std::uniform_real_distribution<float> rd_crop_ratio(lb, ub);
float crop_ratio;
int crop_width, crop_height;
bool crop_success = false;
int64_t input_area = input_height * input_width;
for (auto i = 0; i < max_itr; i++) {
crop_ratio = rd_crop_ratio(rnd);
crop_width = static_cast<int32_t>(std::round(std::sqrt(input_area * static_cast<double>(crop_ratio) / ratio)));
crop_height = static_cast<int32_t>(std::round(crop_width * ratio));
if (crop_width <= input_width && crop_height <= input_height) {
crop_success = true;
break;
}
}
if (crop_success == false) {
ratio = static_cast<float>(input_height) / input_width;
crop_ratio = rd_crop_ratio(rnd);
crop_width = static_cast<int>(std::lround(std::sqrt(input_area * static_cast<double>(crop_ratio) / ratio)));
crop_height = static_cast<int>(std::lround(crop_width * ratio));
crop_height = (crop_height > input_height) ? input_height : crop_height;
crop_width = (crop_width > input_width) ? input_width : crop_width;
}
std::uniform_int_distribution<> rd_x(0, input_width - crop_width);
std::uniform_int_distribution<> rd_y(0, input_height - crop_height);
*crop_box = cv::Rect(rd_x(rnd), rd_y(rnd), crop_width, crop_height);
return Status::OK();
} catch (const cv::Exception &e) {
RETURN_STATUS_UNEXPECTED("error in GenerateRandomCropBox.");
}
}
Status CheckOverlapConstraint(const cv::Rect &crop_box, const std::vector<cv::Rect> &bounding_boxes,
float min_intersect_ratio, bool *is_satisfied) {
try {
// not satisfied if the crop box contains no pixel
if (crop_box.area() < 1.0) {
*is_satisfied = false;
}
for (const auto &b_box : bounding_boxes) {
const float b_box_area = b_box.area();
// not satisfied if the bounding box contains no pixel
if (b_box_area < 1.0) {
continue;
}
const float intersect_ratio = (crop_box & b_box).area() / b_box_area;
if (intersect_ratio >= min_intersect_ratio) {
*is_satisfied = true;
break;
}
}
return Status::OK();
} catch (const cv::Exception &e) {
RETURN_STATUS_UNEXPECTED("error in CheckOverlapConstraint.");
}
}
Status Erase(const std::shared_ptr<Tensor> &input, std::shared_ptr<Tensor> *output, int32_t box_height,
int32_t box_width, int32_t num_patches, bool bounded, bool random_color, uint8_t fill_r, uint8_t fill_g,
uint8_t fill_b) {
int32_t box_width, int32_t num_patches, bool bounded, bool random_color, std::mt19937 *rnd, uint8_t fill_r,
uint8_t fill_g, uint8_t fill_b) {
try {
std::mt19937 rnd;
rnd.seed(GetSeed());
std::shared_ptr<CVTensor> input_cv = CVTensor::AsCVTensor(input);
if (input_cv->mat().data == nullptr || (input_cv->Rank() != 3 && input_cv->shape()[2] != 3)) {
RETURN_STATUS_UNEXPECTED("bad CV Tensor input for erase");
@ -731,8 +665,8 @@ Status Erase(const std::shared_ptr<Tensor> &input, std::shared_ptr<Tensor> *outp
// rows in cv mat refers to the height of the cropped box
// we determine h_start and w_start using two different distributions as erasing is used by two different
// image augmentations. The bounds are also different in each case.
int32_t h_start = (bounded) ? height_distribution_bound(rnd) : (height_distribution_unbound(rnd) - box_height);
int32_t w_start = (bounded) ? width_distribution_bound(rnd) : (width_distribution_unbound(rnd) - box_width);
int32_t h_start = (bounded) ? height_distribution_bound(*rnd) : (height_distribution_unbound(*rnd) - box_height);
int32_t w_start = (bounded) ? width_distribution_bound(*rnd) : (width_distribution_unbound(*rnd) - box_width);
int32_t max_width = (w_start + box_width > image_w) ? image_w : w_start + box_width;
int32_t max_height = (h_start + box_height > image_h) ? image_h : h_start + box_height;
@ -744,9 +678,9 @@ Status Erase(const std::shared_ptr<Tensor> &input, std::shared_ptr<Tensor> *outp
for (int x = h_start; x < max_height; x++) {
if (random_color) {
// fill each box with a random value
input_img.at<cv::Vec3b>(cv::Point(y, x))[0] = static_cast<int32_t>(normal_distribution(rnd));
input_img.at<cv::Vec3b>(cv::Point(y, x))[1] = static_cast<int32_t>(normal_distribution(rnd));
input_img.at<cv::Vec3b>(cv::Point(y, x))[2] = static_cast<int32_t>(normal_distribution(rnd));
input_img.at<cv::Vec3b>(cv::Point(y, x))[0] = static_cast<int32_t>(normal_distribution(*rnd));
input_img.at<cv::Vec3b>(cv::Point(y, x))[1] = static_cast<int32_t>(normal_distribution(*rnd));
input_img.at<cv::Vec3b>(cv::Point(y, x))[2] = static_cast<int32_t>(normal_distribution(*rnd));
} else {
input_img.at<cv::Vec3b>(cv::Point(y, x))[0] = fill_r;
input_img.at<cv::Vec3b>(cv::Point(y, x))[1] = fill_g;

@ -196,12 +196,6 @@ Status AdjustSaturation(const std::shared_ptr<Tensor> &input, std::shared_ptr<Te
// @param output: Adjusted image of same shape and type.
Status AdjustHue(const std::shared_ptr<Tensor> &input, std::shared_ptr<Tensor> *output, const float &hue);
Status GenerateRandomCropBox(int input_height, int input_width, float ratio, float lb, float ub, int max_itr,
cv::Rect *crop_box, uint32_t seed = std::mt19937::default_seed);
Status CheckOverlapConstraint(const cv::Rect &crop_box, const std::vector<cv::Rect> &bounding_boxes,
float min_intersect_ratio, bool *is_satisfied);
// Masks out a random section from the image with set dimension
// @param input: input Tensor
// @param output: cutOut Tensor
@ -214,8 +208,8 @@ Status CheckOverlapConstraint(const cv::Rect &crop_box, const std::vector<cv::Re
// @param fill_g: green fill value for erase
// @param fill_b: blue fill value for erase.
Status Erase(const std::shared_ptr<Tensor> &input, std::shared_ptr<Tensor> *output, int32_t box_height,
int32_t box_width, int32_t num_patches, bool bounded, bool random_color, uint8_t fill_r = 0,
uint8_t fill_g = 0, uint8_t fill_b = 0);
int32_t box_width, int32_t num_patches, bool bounded, bool random_color, std::mt19937 *rnd,
uint8_t fill_r = 0, uint8_t fill_g = 0, uint8_t fill_b = 0);
// Pads the input image and puts the padded image in the output
// @param input: input Tensor

Loading…
Cancel
Save