mirror of
https://github.com/opencv/opencv.git
synced 2026-07-31 00:03:03 +04:00
Merge pull request #28678 from omrope79:caffe-importer-cleanup
Caffe importer cleanup #28678 Merge with: https://github.com/opencv/opencv_extra/pull/1324 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
@@ -1,67 +1,71 @@
|
||||
package org.opencv.test.dnn;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.MatOfByte;
|
||||
import org.opencv.core.Range;
|
||||
import org.opencv.dnn.Dnn;
|
||||
import org.opencv.dnn.Net;
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
|
||||
public class DnnForwardAndRetrieve extends OpenCVTestCase {
|
||||
|
||||
private final static String ENV_OPENCV_DNN_TEST_DATA_PATH = "OPENCV_DNN_TEST_DATA_PATH";
|
||||
private final static String ENV_OPENCV_TEST_DATA_PATH = "OPENCV_TEST_DATA_PATH";
|
||||
|
||||
private String modelFileName = "";
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
String dnnTestDataPath = System.getenv(ENV_OPENCV_DNN_TEST_DATA_PATH);
|
||||
String generalTestDataPath = System.getenv(ENV_OPENCV_TEST_DATA_PATH);
|
||||
|
||||
File model = null;
|
||||
|
||||
if (generalTestDataPath != null) {
|
||||
model = new File(generalTestDataPath, "dnn/onnx/models/split_0.onnx");
|
||||
}
|
||||
|
||||
if ((model == null || !model.isFile()) && dnnTestDataPath != null) {
|
||||
model = new File(dnnTestDataPath, "dnn/onnx/models/split_0.onnx");
|
||||
}
|
||||
|
||||
if (model == null || !model.isFile()) {
|
||||
isTestCaseEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
modelFileName = model.getAbsolutePath();
|
||||
}
|
||||
|
||||
public void testForwardAndRetrieve()
|
||||
{
|
||||
// Create a simple Caffe prototxt with a Slice layer
|
||||
String prototxt =
|
||||
"input: \"data\"\n" +
|
||||
"layer {\n" +
|
||||
" name: \"testLayer\"\n" +
|
||||
" type: \"Slice\"\n" +
|
||||
" bottom: \"data\"\n" +
|
||||
" top: \"firstCopy\"\n" +
|
||||
" top: \"secondCopy\"\n" +
|
||||
" slice_param {\n" +
|
||||
" axis: 0\n" +
|
||||
" slice_point: 2\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
|
||||
// Read network from prototxt
|
||||
MatOfByte bufferProto = new MatOfByte();
|
||||
bufferProto.fromArray(prototxt.getBytes());
|
||||
MatOfByte bufferModel = new MatOfByte();
|
||||
Net net = Dnn.readNetFromCaffe(bufferProto, bufferModel, Dnn.ENGINE_CLASSIC);
|
||||
// Verifies forwardAndRetrieve nested list marshalling using a small ONNX model instead of the removed Caffe importer.
|
||||
Net net = Dnn.readNetFromONNX(modelFileName, Dnn.ENGINE_CLASSIC);
|
||||
net.setPreferableBackend(Dnn.DNN_BACKEND_OPENCV);
|
||||
|
||||
// Create input data
|
||||
Mat inp = new Mat(4, 5, CvType.CV_32F);
|
||||
// split_0.onnx declares a single 4D input named "image" of shape [1, 3, 2, 2].
|
||||
Mat inp = new Mat(new int[]{1, 3, 2, 2}, CvType.CV_32F);
|
||||
Core.randu(inp, -1, 1);
|
||||
net.setInput(inp);
|
||||
|
||||
// Define output names
|
||||
List<String> outNames = new ArrayList<>();
|
||||
outNames.add("testLayer");
|
||||
List<String> outNames = net.getUnconnectedOutLayersNames();
|
||||
assertFalse("Model has no output layers", outNames.isEmpty());
|
||||
|
||||
// Forward and retrieve multiple outputs
|
||||
// Forward and retrieve every output blob of the requested layers.
|
||||
List<List<Mat>> outBlobs = new ArrayList<>();
|
||||
net.forwardAndRetrieve(outBlobs, outNames);
|
||||
|
||||
// Verify results
|
||||
assertEquals(1, outBlobs.size());
|
||||
assertEquals(2, outBlobs.get(0).size());
|
||||
|
||||
// Compare results
|
||||
Mat expectedFirst = inp.rowRange(0, 2);
|
||||
Mat expectedSecond = inp.rowRange(2, 4);
|
||||
|
||||
Mat actualFirst = outBlobs.get(0).get(0);
|
||||
Mat actualSecond = outBlobs.get(0).get(1);
|
||||
|
||||
assertEquals(0, Core.norm(expectedFirst, actualFirst, Core.NORM_INF), EPS);
|
||||
assertEquals(0, Core.norm(expectedSecond, actualSecond, Core.NORM_INF), EPS);
|
||||
// One entry per requested layer name, each holding at least one valid blob.
|
||||
assertEquals(outNames.size(), outBlobs.size());
|
||||
for (List<Mat> blobs : outBlobs) {
|
||||
assertFalse(blobs.isEmpty());
|
||||
for (Mat blob : blobs)
|
||||
assertFalse(blob.empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"whitelist":
|
||||
{
|
||||
"dnn_Net": ["setInput", "forward", "setPreferableBackend","getUnconnectedOutLayersNames"],
|
||||
"": ["readNetFromCaffe", "readNetFromTensorflow", "readNetFromTorch",
|
||||
"": ["readNetFromTensorflow", "readNetFromTorch",
|
||||
"readNetFromONNX", "readNetFromTFLite", "readNet", "blobFromImage"]
|
||||
},
|
||||
"namespace_prefix_override":
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
{
|
||||
"func_arg_fix" : {
|
||||
"Dnn": {
|
||||
"(Net*)readNetFromCaffe:(NSString*)prototxt caffeModel:(NSString*)caffeModel engine:(int)engine" : { "readNetFromCaffe" : {"name" : "readNetFromCaffeFile"} },
|
||||
"(Net*)readNetFromCaffe:(ByteVector*)bufferProto bufferModel:(ByteVector*)bufferModel engine:(int)engine" : { "readNetFromCaffe" : {"name" : "readNetFromCaffeBuffer"} },
|
||||
"(Net*)readNetFromONNX:(NSString*)onnxFile engine:(int)engine" : { "readNetFromONNX" : {"name" : "readNetFromONNXFile"} },
|
||||
"(Net*)readNetFromONNX:(ByteVector*)buffer engine:(int)engine" : { "readNetFromONNX" : {"name" : "readNetFromONNXBuffer"} },
|
||||
"(Net*)readNetFromTensorflow:(NSString*)model config:(NSString*)config engine:(int)engine extraOutputs:(NSArray<NSString*>*)extraOutputs" : { "readNetFromTensorflow" : {"name" : "readNetFromTensorflowFile"} },
|
||||
|
||||
@@ -82,6 +82,13 @@ class dnn_test(NewOpenCVTests):
|
||||
g_dnnBackendsAndTargets = self.initBackendsAndTargets()
|
||||
self.dnnBackendsAndTargets = g_dnnBackendsAndTargets
|
||||
|
||||
def checkIETarget(self, backend, target):
|
||||
# OpenVINO is optional; a target is usable only if its backend lists it.
|
||||
try:
|
||||
return target in cv.dnn.getAvailableTargets(backend)
|
||||
except BaseException:
|
||||
return False
|
||||
|
||||
def initBackendsAndTargets(self):
|
||||
self.dnnBackendsAndTargets = [
|
||||
[cv.dnn.DNN_BACKEND_OPENCV, cv.dnn.DNN_TARGET_CPU],
|
||||
@@ -109,20 +116,6 @@ class dnn_test(NewOpenCVTests):
|
||||
os.environ['OPENCV_TEST_DATA_PATH']],
|
||||
required=required)
|
||||
|
||||
def checkIETarget(self, backend, target):
|
||||
proto = self.find_dnn_file('dnn/layers/layer_convolution.prototxt')
|
||||
model = self.find_dnn_file('dnn/layers/layer_convolution.caffemodel')
|
||||
net = cv.dnn.readNet(proto, model, engine=cv.dnn.ENGINE_CLASSIC)
|
||||
try:
|
||||
net.setPreferableBackend(backend)
|
||||
net.setPreferableTarget(target)
|
||||
inp = np.random.standard_normal([1, 2, 10, 11]).astype(np.float32)
|
||||
net.setInput(inp)
|
||||
net.forward()
|
||||
except BaseException:
|
||||
return False
|
||||
return True
|
||||
|
||||
def test_getAvailableTargets(self):
|
||||
targets = cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_OPENCV)
|
||||
self.assertTrue(cv.dnn.DNN_TARGET_CPU in targets)
|
||||
@@ -219,25 +212,24 @@ class dnn_test(NewOpenCVTests):
|
||||
|
||||
def test_model(self):
|
||||
img_path = self.find_dnn_file("dnn/street.png")
|
||||
weights = self.find_dnn_file("dnn/MobileNetSSD_deploy_19e3ec3.caffemodel", required=False)
|
||||
config = self.find_dnn_file("dnn/MobileNetSSD_deploy_19e3ec3.prototxt", required=False)
|
||||
if weights is None or config is None:
|
||||
raise unittest.SkipTest("Missing DNN test files (dnn/MobileNetSSD_deploy_19e3ec3.{prototxt/caffemodel}). Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
|
||||
weights = self.find_dnn_file("dnn/onnx/models/ssd_vgg16.onnx", required=False)
|
||||
if weights is None:
|
||||
raise unittest.SkipTest("Missing DNN test files (dnn/onnx/models/ssd_vgg16.onnx). Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
|
||||
|
||||
frame = cv.imread(img_path)
|
||||
model = cv.dnn_DetectionModel(weights, config)
|
||||
model.setInputParams(size=(300, 300), mean=(127.5, 127.5, 127.5), scale=1.0/127.5)
|
||||
model = cv.dnn_DetectionModel(weights)
|
||||
model.setInputParams(size=(300, 300), mean=(0, 0, 0), scale=1.0, swapRB=False)
|
||||
|
||||
iouDiff = 0.05
|
||||
confThreshold = 0.0001
|
||||
confThreshold = 0.3
|
||||
nmsThreshold = 0
|
||||
scoreDiff = 1.1e-3
|
||||
scoreDiff = 5e-3
|
||||
|
||||
classIds, confidences, boxes = model.detect(frame, confThreshold, nmsThreshold)
|
||||
|
||||
refClassIds = (7, 15)
|
||||
refConfidences = (0.9998, 0.8793)
|
||||
refBoxes = ((328, 238, 85, 102), (101, 188, 34, 138))
|
||||
refClassIds = (37,)
|
||||
refConfidences = (0.8196,)
|
||||
refBoxes = ((331, 233, 85, 107),)
|
||||
|
||||
normAssertDetections(self, refClassIds, refConfidences, refBoxes,
|
||||
classIds, confidences, boxes,confThreshold, scoreDiff, iouDiff)
|
||||
@@ -251,14 +243,13 @@ class dnn_test(NewOpenCVTests):
|
||||
|
||||
def test_classification_model(self):
|
||||
img_path = self.find_dnn_file("dnn/googlenet_0.png")
|
||||
weights = self.find_dnn_file("dnn/squeezenet_v1.1.caffemodel", required=False)
|
||||
config = self.find_dnn_file("dnn/squeezenet_v1.1.prototxt")
|
||||
weights = self.find_dnn_file("dnn/squeezenet_v1.1.onnx", required=False)
|
||||
ref = np.load(self.find_dnn_file("dnn/squeezenet_v1.1_prob.npy"))
|
||||
if weights is None or config is None:
|
||||
raise unittest.SkipTest("Missing DNN test files (dnn/squeezenet_v1.1.{prototxt/caffemodel}). Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
|
||||
if weights is None:
|
||||
raise unittest.SkipTest("Missing DNN test files (dnn/squeezenet_v1.1.onnx). Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
|
||||
|
||||
frame = cv.imread(img_path)
|
||||
model = cv.dnn_ClassificationModel(config, weights)
|
||||
model = cv.dnn_ClassificationModel(weights)
|
||||
model.setInputSize(227, 227)
|
||||
model.setInputCrop(True)
|
||||
|
||||
@@ -323,54 +314,6 @@ class dnn_test(NewOpenCVTests):
|
||||
normAssertDetections(self, refClassIds, refScores, refBoxes, testClassIds,
|
||||
testScores, testBoxes, 0.5)
|
||||
|
||||
def test_async(self):
|
||||
# bug: https://github.com/opencv/opencv/issues/26376
|
||||
raise unittest.SkipTest("The new dnn engine does not support async inference")
|
||||
|
||||
timeout = 10*1000*10**6 # in nanoseconds (10 sec)
|
||||
proto = self.find_dnn_file('dnn/layers/layer_convolution.prototxt')
|
||||
model = self.find_dnn_file('dnn/layers/layer_convolution.caffemodel')
|
||||
if proto is None or model is None:
|
||||
raise unittest.SkipTest("Missing DNN test files (dnn/layers/layer_convolution.{prototxt/caffemodel}). Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
|
||||
|
||||
print('\n')
|
||||
for backend, target in self.dnnBackendsAndTargets:
|
||||
if backend != cv.dnn.DNN_BACKEND_INFERENCE_ENGINE:
|
||||
continue
|
||||
|
||||
printParams(backend, target)
|
||||
|
||||
netSync = cv.dnn.readNet(proto, model, engine=cv.dnn.ENGINE_CLASSIC)
|
||||
netSync.setPreferableBackend(backend)
|
||||
netSync.setPreferableTarget(target)
|
||||
|
||||
netAsync = cv.dnn.readNet(proto, model)
|
||||
netAsync.setPreferableBackend(backend)
|
||||
netAsync.setPreferableTarget(target)
|
||||
|
||||
# Generate inputs
|
||||
numInputs = 10
|
||||
inputs = []
|
||||
for _ in range(numInputs):
|
||||
inputs.append(np.random.standard_normal([2, 6, 75, 113]).astype(np.float32))
|
||||
|
||||
# Run synchronously
|
||||
refs = []
|
||||
for i in range(numInputs):
|
||||
netSync.setInput(inputs[i])
|
||||
refs.append(netSync.forward())
|
||||
|
||||
# Run asynchronously. To make test more robust, process inputs in the reversed order.
|
||||
outs = []
|
||||
for i in reversed(range(numInputs)):
|
||||
netAsync.setInput(inputs[i])
|
||||
outs.insert(0, netAsync.forwardAsync())
|
||||
|
||||
for i in reversed(range(numInputs)):
|
||||
ret, result = outs[i].get(timeoutNs=float(timeout))
|
||||
self.assertTrue(ret)
|
||||
normAssert(self, refs[i], result, 'Index: %d' % i, 1e-10)
|
||||
|
||||
def test_nms(self):
|
||||
confs = (1, 1)
|
||||
rects = ((0, 0, 0.4, 0.4), (0, 0, 0.2, 0.4)) # 0.5 overlap
|
||||
@@ -401,51 +344,8 @@ class dnn_test(NewOpenCVTests):
|
||||
return [inputs[0][:,:,self.ystart:self.yend,self.xstart:self.xend]]
|
||||
|
||||
cv.dnn_registerLayer('CropCaffe', CropLayer)
|
||||
proto = '''
|
||||
name: "TestCrop"
|
||||
input: "input"
|
||||
input_shape
|
||||
{
|
||||
dim: 1
|
||||
dim: 2
|
||||
dim: 5
|
||||
dim: 5
|
||||
}
|
||||
input: "roi"
|
||||
input_shape
|
||||
{
|
||||
dim: 1
|
||||
dim: 2
|
||||
dim: 3
|
||||
dim: 3
|
||||
}
|
||||
layer {
|
||||
name: "Crop"
|
||||
type: "CropCaffe"
|
||||
bottom: "input"
|
||||
bottom: "roi"
|
||||
top: "Crop"
|
||||
}'''
|
||||
|
||||
net = cv.dnn.readNetFromCaffe(bytearray(proto.encode()))
|
||||
for backend, target in self.dnnBackendsAndTargets:
|
||||
if backend != cv.dnn.DNN_BACKEND_OPENCV:
|
||||
continue
|
||||
|
||||
printParams(backend, target)
|
||||
|
||||
net.setPreferableBackend(backend)
|
||||
net.setPreferableTarget(target)
|
||||
src_shape = [1, 2, 5, 5]
|
||||
dst_shape = [1, 2, 3, 3]
|
||||
inp = np.arange(0, np.prod(src_shape), dtype=np.float32).reshape(src_shape)
|
||||
roi = np.empty(dst_shape, dtype=np.float32)
|
||||
net.setInput(inp, "input")
|
||||
net.setInput(roi, "roi")
|
||||
out = net.forward()
|
||||
ref = inp[:, :, 1:4, 1:4]
|
||||
normAssert(self, out, ref)
|
||||
|
||||
# Skipped: Requires ONNX custom layer multi-input support and Python binding fixes for Net.connect (see #26200).
|
||||
cv.dnn_unregisterLayer('CropCaffe')
|
||||
|
||||
# check that dnn module can work with 3D tensor as input for network
|
||||
|
||||
@@ -33,7 +33,7 @@ assert(not args.quantize or not args.fp16)
|
||||
dtype = tf.float16 if args.fp16 else tf.float32
|
||||
|
||||
################################################################################
|
||||
cvNet = cv.dnn.readNetFromCaffe(args.proto, args.model)
|
||||
cvNet = cv.dnn.readNet(args.proto, args.model)
|
||||
|
||||
def dnnLayer(name):
|
||||
return cvNet.getLayer(long(cvNet.getLayerId(name)))
|
||||
|
||||
Reference in New Issue
Block a user