Make DNN Crop layer match Caffe default offset behavior

and add parametric unit test for crop layer.
This commit is contained in:
David Koller 2018-01-14 00:39:11 -05:00 committed by drkoller
parent a55aed5f42
commit d1a3b530be
2 changed files with 87 additions and 18 deletions

View File

@ -110,27 +110,13 @@ public:
} }
crop_ranges.resize(dims, Range::all()); crop_ranges.resize(dims, Range::all());
for (int i = 0; i < dims; i++) for (int i = start_axis; i < dims; i++)
{
if( i < start_axis )
continue;
if (!offset.empty()) //normal case
{ {
if (offset_final[i] < 0 || offset_final[i] + inpSzBlob.size[i] > inpBlob.size[i]) if (offset_final[i] < 0 || offset_final[i] + inpSzBlob.size[i] > inpBlob.size[i])
CV_Error(Error::StsBadArg, "invalid crop parameters"); CV_Error(Error::StsBadArg, "invalid crop parameters or blob sizes");
crop_ranges[i] = Range(offset_final[i], offset_final[i] + inpSzBlob.size[i]); crop_ranges[i] = Range(offset_final[i], offset_final[i] + inpSzBlob.size[i]);
} }
else //detect offset automatically so that cropped image is center of original one
{
if (inpSzBlob.size[i] > inpBlob.size[i])
CV_Error(Error::StsBadArg, "invalid output blob size");
int cur_crop = (inpBlob.size[i] - inpSzBlob.size[i]) / 2;
crop_ranges[i] = Range(cur_crop, cur_crop + inpSzBlob.size[i]);
}
}
} }
void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr)

View File

@ -715,4 +715,87 @@ INSTANTIATE_TEST_CASE_P(Layer_Test, Scale_untrainable, Combine(
/*conv fusion*/ testing::Bool() /*conv fusion*/ testing::Bool()
)); ));
typedef testing::TestWithParam<tuple<Vec4i, Vec4i, int, int, int> > Crop;
TEST_P(Crop, Accuracy)
{
Vec4i inpShapeVec = get<0>(GetParam());
Vec4i sizShapeVec = get<1>(GetParam());
int axis = get<2>(GetParam());
int numOffsets = get<3>(GetParam());
int offsetVal = get<4>(GetParam());
const int inpShape[] = {inpShapeVec[0], inpShapeVec[1], inpShapeVec[2], inpShapeVec[3]};
const int sizShape[] = {sizShapeVec[0], sizShapeVec[1], sizShapeVec[2], sizShapeVec[3]};
// Create a network with two inputs. Crop layer crops a first input to
// the size of a second one.
// See http://caffe.berkeleyvision.org/tutorial/layers/crop.html
Net net;
LayerParams lp;
lp.name = "testCrop";
lp.type = "Crop";
lp.set("axis", axis);
if (numOffsets > 0)
{
std::vector<int> offsets(numOffsets, offsetVal);
lp.set("offset", DictValue::arrayInt<int*>(&offsets[0], offsets.size()));
}
else
offsetVal = 0;
int id = net.addLayerToPrev(lp.name, lp.type, lp);
net.connect(0, 1, id, 1);
Mat inpImage(4, inpShape, CV_32F);
Mat sizImage(4, sizShape, CV_32F);
randu(inpImage, -1, 1);
randu(sizImage, -1, 1);
std::vector<String> inpNames(2);
inpNames[0] = "cropImage";
inpNames[1] = "sizImage";
net.setInputsNames(inpNames);
net.setInput(inpImage, inpNames[0]);
net.setInput(sizImage, inpNames[1]);
// There are a few conditions that represent invalid input to the crop
// layer, so in those cases we want to verify an exception is thrown.
bool shouldThrowException = false;
if (numOffsets > 1 && numOffsets != 4 - axis)
shouldThrowException = true;
else
for (int i = axis; i < 4; i++)
if (sizShape[i] + offsetVal > inpShape[i])
shouldThrowException = true;
Mat out;
if (shouldThrowException)
{
ASSERT_ANY_THROW(out = net.forward());
return;
}
else
out = net.forward();
// Finally, compare the cropped output blob from the DNN layer (out)
// to a reference blob (ref) that we compute here.
std::vector<Range> crop_range;
crop_range.resize(4, Range::all());
for (int i = axis; i < 4; i++)
crop_range[i] = Range(offsetVal, sizShape[i] + offsetVal);
Mat ref(sizImage.dims, sizImage.size, CV_32F);
inpImage(&crop_range[0]).copyTo(ref);
normAssert(out, ref);
}
INSTANTIATE_TEST_CASE_P(Layer_Test, Crop, Combine(
/*input blob shape*/ Values(Vec4i(1, 3, 20, 30)),
/*cropsize blob shape*/ Values(Vec4i(1, 3, 10, 12)),
/*start axis*/ Values(0, 1, 2),
/*number of offsets*/ Values(0, 1, 2, 4),
/*offset value*/ Values(3, 4)
));
} }