Merge remote-tracking branch 'upstream/3.4' into merge-3.4

This commit is contained in:
Alexander Alekhin 2021-08-14 18:24:00 +00:00
commit 0c01cf7c85
10 changed files with 245 additions and 50 deletions

View File

@ -462,7 +462,7 @@ public:
// Retrieve all prior bboxes
std::vector<util::NormalizedBBox> priorBBoxes;
std::vector<std::vector<float> > priorVariances;
GetPriorBBoxes(priorData, numPriors, _bboxesNormalized, priorBBoxes, priorVariances);
GetPriorBBoxes(priorData, numPriors, _bboxesNormalized, _varianceEncodedInTarget, priorBBoxes, priorVariances);
// Decode all loc predictions to bboxes
util::NormalizedBBox clipBounds;
@ -756,7 +756,7 @@ public:
CV_Assert(prior_bboxes.size() == prior_variances.size());
CV_Assert(prior_bboxes.size() == bboxes.size());
size_t num_bboxes = prior_bboxes.size();
CV_Assert(num_bboxes == 0 || prior_variances[0].size() == 4);
CV_Assert(num_bboxes == 0 || prior_variances[0].size() == 4 || variance_encoded_in_target);
decode_bboxes.clear(); decode_bboxes.resize(num_bboxes);
if(variance_encoded_in_target)
{
@ -808,12 +808,13 @@ public:
}
// Get prior bounding boxes from prior_data
// prior_data: 1 x 2 x num_priors * 4 x 1 blob.
// prior_data: 1 x 1 x num_priors * 4 x 1 blob or 1 x 2 x num_priors * 4 x 1 blob.
// num_priors: number of priors.
// prior_bboxes: stores all the prior bboxes in the format of util::NormalizedBBox.
// prior_variances: stores all the variances needed by prior bboxes.
static void GetPriorBBoxes(const float* priorData, const int& numPriors,
bool normalized_bbox, std::vector<util::NormalizedBBox>& priorBBoxes,
bool normalized_bbox, bool variance_encoded_in_target,
std::vector<util::NormalizedBBox>& priorBBoxes,
std::vector<std::vector<float> >& priorVariances)
{
priorBBoxes.clear(); priorBBoxes.resize(numPriors);
@ -829,13 +830,16 @@ public:
bbox.set_size(BBoxSize(bbox, normalized_bbox));
}
for (int i = 0; i < numPriors; ++i)
if (!variance_encoded_in_target)
{
int startIdx = (numPriors + i) * 4;
// not needed here: priorVariances[i].clear();
for (int j = 0; j < 4; ++j)
for (int i = 0; i < numPriors; ++i)
{
priorVariances[i].push_back(priorData[startIdx + j]);
int startIdx = (numPriors + i) * 4;
// not needed here: priorVariances[i].clear();
for (int j = 0; j < 4; ++j)
{
priorVariances[i].push_back(priorData[startIdx + j]);
}
}
}
}

View File

@ -80,12 +80,31 @@ static void sigmoid(const Mat &src, Mat &dst)
cv::pow(1 + dst, -1, dst);
}
typedef void (*ActivationFunction)(const Mat &src, Mat &dst);
static ActivationFunction get_activation_function(const String& activation) {
// most used activations for PyTorch and TF : Tanh, Sigmoid
// if you need to support more optional activations use std::map instead
if (activation == "Tanh")
{
return tanh;
}
else if (activation == "Sigmoid")
{
return sigmoid;
}
else
{
CV_Error(Error::StsNotImplemented,
cv::format("Activation function [%s] for layer LSTM is not supported", activation.c_str()));
}
}
class LSTMLayerImpl CV_FINAL : public LSTMLayer
{
int numTimeStamps, numSamples;
bool allocated;
MatShape outTailShape; //shape of single output sample
MatShape outTailShape; //shape of single output sample
MatShape outTsShape; //shape of N output samples
bool useTimestampDim;
@ -95,6 +114,10 @@ class LSTMLayerImpl CV_FINAL : public LSTMLayer
bool reverse; // If true, go in negative direction along the time axis
bool bidirectional; // If true, produces both forward and reversed directions along time axis
ActivationFunction f_activation;
ActivationFunction g_activation;
ActivationFunction h_activation;
public:
LSTMLayerImpl(const LayerParams& params)
@ -145,6 +168,20 @@ public:
reverse = params.get<bool>("reverse", false);
CV_Assert(!reverse || !bidirectional);
// read activations
DictValue activations = params.get<DictValue>("activations", "");
if (activations.size() == 1) // if activations wasn't specified use default
{
f_activation = sigmoid;
g_activation = tanh;
h_activation = tanh;
} else {
CV_Assert(activations.size() == 3);
f_activation = get_activation_function(activations.getStringValue(0));
g_activation = get_activation_function(activations.getStringValue(1));
h_activation = get_activation_function(activations.getStringValue(2));
}
allocated = false;
outTailShape.clear();
}
@ -339,15 +376,15 @@ public:
Mat gatesIF = gates.colRange(0, 2*numOut);
gemm(cInternal, blobs[5], 1, gateI, 1, gateI);
gemm(cInternal, blobs[6], 1, gateF, 1, gateF);
sigmoid(gatesIF, gatesIF);
f_activation(gatesIF, gatesIF);
}
else
{
Mat gatesIFO = gates.colRange(0, 3*numOut);
sigmoid(gatesIFO, gatesIFO);
f_activation(gatesIFO, gatesIFO);
}
tanh(gateG, gateG);
g_activation(gateG, gateG);
//compute c_t
multiply(gateF, cInternal, gateF); // f_t (*) c_{t-1}
@ -362,11 +399,11 @@ public:
if (usePeephole)
{
gemm(cInternal, blobs[7], 1, gateO, 1, gateO);
sigmoid(gateO, gateO);
f_activation(gateO, gateO);
}
//compute h_t
tanh(cInternal, hInternal);
h_activation(cInternal, hInternal);
multiply(gateO, hInternal, hInternal);
//save results in output blobs

View File

@ -254,6 +254,10 @@ static DictValue parse(const ::google::protobuf::RepeatedField< ::google::protob
return DictValue::arrayInt(&dst[0], src.size());
}
static DictValue parseStr(const ::google::protobuf::RepeatedPtrField< ::std::string>& src) {
return DictValue::arrayString(src.begin(), static_cast<int>(src.size()));
}
LayerParams ONNXImporter::getLayerParams(const opencv_onnx::NodeProto& node_proto)
{
LayerParams lp;
@ -313,6 +317,10 @@ LayerParams ONNXImporter::getLayerParams(const opencv_onnx::NodeProto& node_prot
CV_Assert(attribute_proto.ints_size() == 1 || attribute_proto.ints_size() == 2 || attribute_proto.ints_size() == 3);
lp.set("dilation", parse(attribute_proto.ints()));
}
else if(attribute_name == "activations" && node_proto.op_type() == "LSTM")
{
lp.set(attribute_name, parseStr(attribute_proto.strings()));
}
else if (attribute_proto.has_i())
{
::google::protobuf::int64 src = attribute_proto.i();
@ -1176,18 +1184,32 @@ void ONNXImporter::parseLSTM(LayerParams& layerParams, const opencv_onnx::NodePr
lstmParams.name += "/lstm";
// https://pytorch.org/docs/stable/nn.html#lstm
CV_Assert(node_proto.input_size() == 7);
CV_Assert(node_proto.input_size() >= 7);
Mat Wx = getBlob(node_proto, 1);
Mat Wh = getBlob(node_proto, 2);
Mat b = getBlob(node_proto, 3);
Mat h0 = getBlob(node_proto, 5);
Mat c0 = getBlob(node_proto, 6);
b = b.reshape(1, b.size[0]);
const int numHidden = lstmParams.get<int>("hidden_size");
const int numDirs = Wx.size[0]; // Is 1 for forward only and 2 for bidirectional LSTM.
const int numFeatures = Wx.size[2];
Mat h0, c0;
if (!node_proto.input(5).empty()) {
h0 = getBlob(node_proto, 5);
h0 = h0.reshape(1, h0.size[0] * h0.size[1]);
} else {
// initial_h attribute can be empty in case of keras2onnx producer. fill it with zeros
h0 = Mat::zeros(numDirs * numFeatures, numHidden, CV_32FC1);
}
if (!node_proto.input(6).empty()) {
c0 = getBlob(node_proto, 6);
c0 = c0.reshape(1, c0.size[0] * c0.size[1]);
} else {
// initial_c attribute can be empty in case of keras2onnx producer. fill it with zeros
c0 = Mat::zeros(numDirs * numFeatures, numHidden, CV_32FC1);
}
b = b.reshape(1, b.size[0]);
Mat bx = b.colRange(0, b.cols / 2);
Mat bh = b.colRange(b.cols / 2, b.cols);
b = bx + bh;
@ -1215,8 +1237,7 @@ void ONNXImporter::parseLSTM(LayerParams& layerParams, const opencv_onnx::NodePr
}
Wx = Wx.reshape(1, Wx.size[0] * Wx.size[1]);
Wh = Wh.reshape(1, Wh.size[0] * Wh.size[1]);
h0 = h0.reshape(1, h0.size[0] * h0.size[1]);
c0 = c0.reshape(1, c0.size[0] * c0.size[1]);
lstmParams.blobs.resize(5);
lstmParams.blobs[0] = Wh;
@ -1224,6 +1245,9 @@ void ONNXImporter::parseLSTM(LayerParams& layerParams, const opencv_onnx::NodePr
lstmParams.blobs[2] = b;
lstmParams.blobs[3] = h0;
lstmParams.blobs[4] = c0;
// read direction attribute
lstmParams.set("reverse", lstmParams.get<String>("direction", "") == "reverse");
lstmParams.set("bidirectional", lstmParams.get<String>("direction", "") == "bidirectional");
node_proto.set_output(0, lstmParams.name); // set different name so output shapes will be registered on that name

View File

@ -668,7 +668,7 @@ const TFImporter::DispatchMap TFImporter::buildDispatchMap()
dispatch["PriorBox"] = &TFImporter::parsePriorBox;
dispatch["Softmax"] = &TFImporter::parseSoftmax;
dispatch["CropAndResize"] = &TFImporter::parseCropAndResize;
dispatch["Mean"] = dispatch["Sum"] = &TFImporter::parseMean;
dispatch["Mean"] = dispatch["Sum"] = dispatch["Max"] = &TFImporter::parseMean;
dispatch["Pack"] = &TFImporter::parsePack;
dispatch["ClipByValue"] = &TFImporter::parseClipByValue;
dispatch["LeakyRelu"] = &TFImporter::parseLeakyRelu;
@ -678,6 +678,7 @@ const TFImporter::DispatchMap TFImporter::buildDispatchMap()
return dispatch;
}
// "Conv2D" "SpaceToBatchND" "DepthwiseConv2dNative" "Pad" "MirrorPad" "Conv3D"
void TFImporter::parseConvolution(tensorflow::GraphDef& net, const tensorflow::NodeDef& layer_, LayerParams& layerParams)
{
tensorflow::NodeDef layer = layer_;
@ -897,6 +898,7 @@ void TFImporter::parseConvolution(tensorflow::GraphDef& net, const tensorflow::N
data_layouts[name] = DATA_LAYOUT_NHWC;
}
// "BiasAdd" "Add" "AddV2" "Sub" "AddN"
void TFImporter::parseBias(tensorflow::GraphDef& net, const tensorflow::NodeDef& layer, LayerParams& layerParams)
{
const std::string& name = layer.name();
@ -1108,6 +1110,7 @@ void TFImporter::parseReshape(tensorflow::GraphDef& net, const tensorflow::NodeD
}
}
// "Flatten" "Squeeze"
void TFImporter::parseFlatten(tensorflow::GraphDef& net, const tensorflow::NodeDef& layer, LayerParams& layerParams)
{
const std::string& name = layer.name();
@ -1266,6 +1269,7 @@ void TFImporter::parseLrn(tensorflow::GraphDef& net, const tensorflow::NodeDef&
connectToAllBlobs(layer_id, dstNet, parsePin(layer.input(0)), id, num_inputs);
}
// "Concat" "ConcatV2"
void TFImporter::parseConcat(tensorflow::GraphDef& net, const tensorflow::NodeDef& layer, LayerParams& layerParams)
{
const std::string& name = layer.name();
@ -1316,6 +1320,7 @@ void TFImporter::parseConcat(tensorflow::GraphDef& net, const tensorflow::NodeDe
}
}
// "MaxPool" "MaxPool3D"
void TFImporter::parseMaxPool(tensorflow::GraphDef& net, const tensorflow::NodeDef& layer, LayerParams& layerParams)
{
const std::string& name = layer.name();
@ -1337,6 +1342,7 @@ void TFImporter::parseMaxPool(tensorflow::GraphDef& net, const tensorflow::NodeD
connectToAllBlobs(layer_id, dstNet, parsePin(inputName), id, num_inputs);
}
// "AvgPool" "AvgPool3D"
void TFImporter::parseAvgPool(tensorflow::GraphDef& net, const tensorflow::NodeDef& layer, LayerParams& layerParams)
{
const std::string& name = layer.name();
@ -1523,6 +1529,7 @@ void TFImporter::parseStridedSlice(tensorflow::GraphDef& net, const tensorflow::
connect(layer_id, dstNet, parsePin(layer.input(0)), id, 0);
}
// "Mul" "RealDiv"
void TFImporter::parseMul(tensorflow::GraphDef& net, const tensorflow::NodeDef& layer, LayerParams& layerParams)
{
const std::string& name = layer.name();
@ -1680,6 +1687,7 @@ void TFImporter::parseMul(tensorflow::GraphDef& net, const tensorflow::NodeDef&
}
}
// "FusedBatchNorm" "FusedBatchNormV3"
void TFImporter::parseFusedBatchNorm(tensorflow::GraphDef& net, const tensorflow::NodeDef& layer, LayerParams& layerParams)
{
// op: "FusedBatchNorm"
@ -1939,6 +1947,7 @@ void TFImporter::parseBlockLSTM(tensorflow::GraphDef& net, const tensorflow::Nod
data_layouts[name] = DATA_LAYOUT_UNKNOWN;
}
// "ResizeNearestNeighbor" "ResizeBilinear" "FusedResizeAndPadConv2D"
void TFImporter::parseResize(tensorflow::GraphDef& net, const tensorflow::NodeDef& layer_, LayerParams& layerParams)
{
tensorflow::NodeDef layer = layer_;
@ -2127,6 +2136,7 @@ void TFImporter::parseCropAndResize(tensorflow::GraphDef& net, const tensorflow:
connect(layer_id, dstNet, parsePin(layer.input(1)), id, 1);
}
// "Mean" "Sum" "Max"
void TFImporter::parseMean(tensorflow::GraphDef& net, const tensorflow::NodeDef& layer, LayerParams& layerParams)
{
// Computes the mean of elements across dimensions of a tensor.
@ -2145,7 +2155,12 @@ void TFImporter::parseMean(tensorflow::GraphDef& net, const tensorflow::NodeDef&
const std::string& name = layer.name();
const std::string& type = layer.op();
const int num_inputs = layer.input_size();
std::string pool_type = cv::toLowerCase(type);
if (pool_type == "mean")
{
pool_type = "ave";
}
CV_CheckGT(num_inputs, 0, "");
Mat indices = getTensorContent(getConstBlob(layer, value_id, 1));
@ -2182,7 +2197,7 @@ void TFImporter::parseMean(tensorflow::GraphDef& net, const tensorflow::NodeDef&
LayerParams avgLp;
std::string avgName = name + "/avg";
CV_Assert(layer_id.find(avgName) == layer_id.end());
avgLp.set("pool", type == "Mean" ? "ave" : "sum");
avgLp.set("pool", pool_type);
// pooling kernel H x 1
avgLp.set("global_pooling_h", true);
avgLp.set("kernel_w", 1);
@ -2223,7 +2238,7 @@ void TFImporter::parseMean(tensorflow::GraphDef& net, const tensorflow::NodeDef&
int axis = toNCHW(indices.at<int>(0));
if (axis == 2 || axis == 3)
{
layerParams.set("pool", type == "Mean" ? "ave" : "sum");
layerParams.set("pool", pool_type);
layerParams.set(axis == 2 ? "kernel_w" : "kernel_h", 1);
layerParams.set(axis == 2 ? "global_pooling_h" : "global_pooling_w", true);
int id = dstNet.addLayer(name, "Pooling", layerParams);
@ -2255,7 +2270,7 @@ void TFImporter::parseMean(tensorflow::GraphDef& net, const tensorflow::NodeDef&
Pin inpId = parsePin(layer.input(0));
addPermuteLayer(order, name + "/nhwc", inpId);
layerParams.set("pool", type == "Mean" ? "ave" : "sum");
layerParams.set("pool", pool_type);
layerParams.set("kernel_h", 1);
layerParams.set("global_pooling_w", true);
int id = dstNet.addLayer(name, "Pooling", layerParams);
@ -2285,7 +2300,7 @@ void TFImporter::parseMean(tensorflow::GraphDef& net, const tensorflow::NodeDef&
if (indices.total() != 2 || indices.at<int>(0) != 1 || indices.at<int>(1) != 2)
CV_Error(Error::StsNotImplemented, "Unsupported mode of reduce_mean or reduce_sum operation.");
layerParams.set("pool", type == "Mean" ? "ave" : "sum");
layerParams.set("pool", pool_type);
layerParams.set("global_pooling", true);
int id = dstNet.addLayer(name, "Pooling", layerParams);
layer_id[name] = id;
@ -2389,6 +2404,7 @@ void TFImporter::parseLeakyRelu(tensorflow::GraphDef& net, const tensorflow::Nod
connectToAllBlobs(layer_id, dstNet, parsePin(layer.input(0)), id, num_inputs);
}
// "Abs" "Tanh" "Sigmoid" "Relu" "Elu" "Exp" "Identity" "Relu6"
void TFImporter::parseActivation(tensorflow::GraphDef& net, const tensorflow::NodeDef& layer, LayerParams& layerParams)
{
const std::string& name = layer.name();

View File

@ -700,6 +700,11 @@ TEST_P(Test_ONNX_layers, Split_EltwiseMax)
testONNXModels("split_max");
}
TEST_P(Test_ONNX_layers, LSTM_Activations)
{
testONNXModels("lstm_cntk_tanh", pb, 0, 0, false, false);
}
TEST_P(Test_ONNX_layers, LSTM)
{
testONNXModels("lstm", npy, 0, 0, false, false);

View File

@ -128,6 +128,13 @@ TEST_P(Test_TensorFlow_layers, reduce_mean)
runTensorFlowNet("global_pool_by_axis");
}
TEST_P(Test_TensorFlow_layers, reduce_max)
{
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
runTensorFlowNet("max_pool_by_axis");
}
TEST_P(Test_TensorFlow_layers, reduce_sum)
{
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
@ -135,11 +142,21 @@ TEST_P(Test_TensorFlow_layers, reduce_sum)
runTensorFlowNet("sum_pool_by_axis");
}
TEST_P(Test_TensorFlow_layers, reduce_max_channel)
{
runTensorFlowNet("reduce_max_channel");
}
TEST_P(Test_TensorFlow_layers, reduce_sum_channel)
{
runTensorFlowNet("reduce_sum_channel");
}
TEST_P(Test_TensorFlow_layers, reduce_max_channel_keep_dims)
{
runTensorFlowNet("reduce_max_channel", false, 0.0, 0.0, false, "_keep_dims");
}
TEST_P(Test_TensorFlow_layers, reduce_sum_channel_keep_dims)
{
runTensorFlowNet("reduce_sum_channel", false, 0.0, 0.0, false, "_keep_dims");
@ -395,6 +412,11 @@ TEST_P(Test_TensorFlow_layers, pooling_reduce_mean)
runTensorFlowNet("reduce_mean"); // an average pooling over all spatial dimensions.
}
TEST_P(Test_TensorFlow_layers, pooling_reduce_max)
{
runTensorFlowNet("reduce_max"); // a MAX pooling over all spatial dimensions.
}
TEST_P(Test_TensorFlow_layers, pooling_reduce_sum)
{
runTensorFlowNet("reduce_sum"); // a SUM pooling over all spatial dimensions.

View File

@ -2303,7 +2303,7 @@ enlarge an image, it will generally look best with c#INTER_CUBIC (slow) or #INTE
@param src input image.
@param dst output image; it has the size dsize (when it is non-zero) or the size computed from
src.size(), fx, and fy; the type of dst is the same as of src.
@param dsize output image size; if it equals zero, it is computed as:
@param dsize output image size; if it equals zero (`None` in Python), it is computed as:
\f[\texttt{dsize = Size(round(fx*src.cols), round(fy*src.rows))}\f]
Either dsize or both fx and fy must be non-zero.
@param fx scale factor along the horizontal axis; when it equals 0, it is computed as
@ -3897,6 +3897,7 @@ hierarchy[i][0] , hierarchy[i][1] , hierarchy[i][2] , and hierarchy[i][3] are se
in contours of the next and previous contours at the same hierarchical level, the first child
contour and the parent contour, respectively. If for the contour i there are no next, previous,
parent, or nested contours, the corresponding elements of hierarchy[i] will be negative.
@note In Python, hierarchy is nested inside a top level array. Use hierarchy[0][i] to access hierarchical elements of i-th contour.
@param mode Contour retrieval mode, see #RetrievalModes
@param method Contour approximation method, see #ContourApproximationModes
@param offset Optional offset by which every contour point is shifted. This is useful if the

View File

@ -88,6 +88,32 @@ enum { CALIPERS_MAXHEIGHT=0, CALIPERS_MINAREARECT=1, CALIPERS_MAXDIST=2 };
// Notes:
//F*/
static void rotate90CCW(const cv::Point2f& in, cv::Point2f &out)
{
out.x = -in.y;
out.y = in.x;
}
static void rotate90CW(const cv::Point2f& in, cv::Point2f &out)
{
out.x = in.y;
out.y = -in.x;
}
static void rotate180(const cv::Point2f& in, cv::Point2f &out)
{
out.x = -in.x;
out.y = -in.y;
}
/* return true if first vector is to the right (clockwise) of the second */
static bool firstVecIsRight(const cv::Point2f& vec1, const cv::Point2f &vec2)
{
cv::Point2f tmp;
rotate90CW(vec1, tmp);
return tmp.x * vec2.x + tmp.y * vec2.y < 0;
}
/* we will use usual cartesian coordinates */
static void rotatingCalipers( const Point2f* points, int n, int mode, float* out )
{
@ -100,6 +126,7 @@ static void rotatingCalipers( const Point2f* points, int n, int mode, float* out
Point2f* vect = (Point2f*)(inv_vect_length + n);
int left = 0, bottom = 0, right = 0, top = 0;
int seq[4] = { -1, -1, -1, -1 };
Point2f rot_vect[4];
/* rotating calipers sides will always have coordinates
(a,b) (-b,a) (-a,-b) (b, -a)
@ -179,32 +206,18 @@ static void rotatingCalipers( const Point2f* points, int n, int mode, float* out
/* all of edges will be checked while rotating calipers by 90 degrees */
for( k = 0; k < n; k++ )
{
/* sinus of minimal angle */
/*float sinus;*/
/* compute cosine of angle between calipers side and polygon edge */
/* dp - dot product */
float dp[4] = {
+base_a * vect[seq[0]].x + base_b * vect[seq[0]].y,
-base_b * vect[seq[1]].x + base_a * vect[seq[1]].y,
-base_a * vect[seq[2]].x - base_b * vect[seq[2]].y,
+base_b * vect[seq[3]].x - base_a * vect[seq[3]].y,
};
float maxcos = dp[0] * inv_vect_length[seq[0]];
/* number of calipers edges, that has minimal angle with edge */
int main_element = 0;
/* choose minimal angle */
for ( i = 1; i < 4; ++i )
/* choose minimum angle between calipers side and polygon edge by dot product sign */
rot_vect[0] = vect[seq[0]];
rotate90CW(vect[seq[1]], rot_vect[1]);
rotate180(vect[seq[2]], rot_vect[2]);
rotate90CCW(vect[seq[3]], rot_vect[3]);
for (i = 1; i < 4; i++)
{
float cosalpha = dp[i] * inv_vect_length[seq[i]];
if (cosalpha > maxcos)
{
if (firstVecIsRight(rot_vect[i], rot_vect[main_element]))
main_element = i;
maxcos = cosalpha;
}
}
/*rotate calipers*/

View File

@ -2384,5 +2384,78 @@ TEST(Imgproc_minAreaRect, reproducer_18157)
EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle;
}
TEST(Imgproc_minAreaRect, reproducer_19769_lightweight)
{
const int N = 23;
float pts_[N][2] = {
{1325, 732}, {1248, 808}, {582, 1510}, {586, 1524},
{595, 1541}, {599, 1547}, {789, 1745}, {829, 1786},
{997, 1958}, {1116, 2074}, {1207, 2066}, {1216, 2058},
{1231, 2044}, {1265, 2011}, {2036, 1254}, {2100, 1191},
{2169, 1123}, {2315, 979}, {2395, 900}, {2438, 787},
{2434, 782}, {2416, 762}, {2266, 610}
};
Mat contour(N, 1, CV_32FC2, (void*)pts_);
RotatedRect rr = cv::minAreaRect(contour);
EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle;
}
TEST(Imgproc_minAreaRect, reproducer_19769)
{
const int N = 169;
float pts_[N][2] = {
{1854, 227}, {1850, 228}, {1847, 229}, {1835, 235},
{1832, 237}, {1829, 239}, {1825, 242}, {1818, 248},
{1807, 258}, {1759, 306}, {1712, 351}, {1708, 356},
{1658, 404}, {1655, 408}, {1602, 459}, {1599, 463},
{1542, 518}, {1477, 582}, {1402, 656}, {1325, 732},
{1248, 808}, {1161, 894}, {1157, 898}, {1155, 900},
{1068, 986}, {1060, 995}, {1058, 997}, {957, 1097},
{956, 1097}, {814, 1238}, {810, 1242}, {805, 1248},
{610, 1442}, {603, 1450}, {599, 1455}, {596, 1459},
{594, 1462}, {592, 1465}, {590, 1470}, {588, 1472},
{586, 1476}, {586, 1478}, {584, 1481}, {583, 1485},
{582, 1490}, {582, 1510}, {583, 1515}, {584, 1518},
{585, 1521}, {586, 1524}, {593, 1538}, {595, 1541},
{597, 1544}, {599, 1547}, {603, 1552}, {609, 1559},
{623, 1574}, {645, 1597}, {677, 1630}, {713, 1667},
{753, 1707}, {789, 1744}, {789, 1745}, {829, 1786},
{871, 1828}, {909, 1867}, {909, 1868}, {950, 1910},
{953, 1912}, {997, 1958}, {1047, 2009}, {1094, 2056},
{1105, 2066}, {1110, 2070}, {1113, 2072}, {1116, 2074},
{1119, 2076}, {1122, 2077}, {1124, 2079}, {1130, 2082},
{1133, 2083}, {1136, 2084}, {1139, 2085}, {1142, 2086},
{1148, 2087}, {1166, 2087}, {1170, 2086}, {1174, 2085},
{1177, 2084}, {1180, 2083}, {1188, 2079}, {1190, 2077},
{1193, 2076}, {1196, 2074}, {1199, 2072}, {1202, 2070},
{1207, 2066}, {1216, 2058}, {1231, 2044}, {1265, 2011},
{1314, 1962}, {1360, 1917}, {1361, 1917}, {1408, 1871},
{1457, 1822}, {1508, 1773}, {1512, 1768}, {1560, 1722},
{1617, 1665}, {1671, 1613}, {1730, 1554}, {1784, 1502},
{1786, 1500}, {1787, 1498}, {1846, 1440}, {1850, 1437},
{1908, 1380}, {1974, 1314}, {2034, 1256}, {2036, 1254},
{2100, 1191}, {2169, 1123}, {2242, 1051}, {2315, 979},
{2395, 900}, {2426, 869}, {2435, 859}, {2438, 855},
{2440, 852}, {2442, 849}, {2443, 846}, {2445, 844},
{2446, 842}, {2446, 840}, {2448, 837}, {2449, 834},
{2450, 829}, {2450, 814}, {2449, 809}, {2448, 806},
{2447, 803}, {2442, 793}, {2440, 790}, {2438, 787},
{2434, 782}, {2428, 775}, {2416, 762}, {2411, 758},
{2342, 688}, {2340, 686}, {2338, 684}, {2266, 610},
{2260, 605}, {2170, 513}, {2075, 417}, {2073, 415},
{2069, 412}, {1955, 297}, {1955, 296}, {1913, 254},
{1904, 246}, {1897, 240}, {1894, 238}, {1891, 236},
{1888, 234}, {1880, 230}, {1877, 229}, {1874, 228},
{1870, 227}
};
Mat contour(N, 1, CV_32FC2, (void*)pts_);
RotatedRect rr = cv::minAreaRect(contour);
EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle;
}
}} // namespace
/* End of file. */

View File

@ -36,7 +36,7 @@ public class MLTest extends OpenCVTestCase {
String filename = OpenCVTestRunner.getTempFileName("yml");
saved.save(filename);
SVM loaded = SVM.load(filename);
assertTrue(saved.isTrained());
assertTrue(loaded.isTrained());
}
}