diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..73bee79529b0c61be6d32d1f698cd6b969326428 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +ssd_mobilenet_v2_coco_2018_03_29/demo + +**/demo diff --git a/efficientdet-d0/LICENSE b/efficientdet-d0/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..51c79d9ae037822dda676b455f685345b7effdb6 --- /dev/null +++ b/efficientdet-d0/LICENSE @@ -0,0 +1,203 @@ +Copyright 2020 Google Research. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020, Google Research. + + 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. diff --git a/efficientdet-d0/README.md b/efficientdet-d0/README.md new file mode 100644 index 0000000000000000000000000000000000000000..147dc3157766be833a56300dc0e48f9f426c8ad6 --- /dev/null +++ b/efficientdet-d0/README.md @@ -0,0 +1,62 @@ +# EfficientDet-D0 + +Object detection with EfficientDet-D0 trained on COCO. The model was originally +distributed as a frozen TensorFlow graph (`efficientdet-d0.pb`) and converted to +ONNX for use with OpenCV's DNN module. This is a **backbone-only** export: the +graph emits raw class logits and box regressions, while anchor generation, sigmoid, +box decoding and non-maximum suppression are performed in host code (see the demos). + +## Model Details +- **Architecture**: EfficientDet-D0 +- **Input**: RGB image, 512×512, raw uint8, NHWC layout (`image_arrays:0`, shape `[1, 512, 512, 3]`) +- **Output**: raw class logits (`concat:0`, shape `[1, 49104, 90]`) and box regression (`concat_1:0`, shape `[1, 49104, 4]`); anchor decode + NMS are done in host code, not in the graph +- **Framework**: ONNX (converted from the TensorFlow frozen graph via tf2onnx, opset 18) +- **Original weights**: https://www.dropbox.com/s/9mqp99fd2tpuqn6/efficientdet-d0.pb?dl=1 + +The graph outputs are per-anchor predictions only. The demos build the 49104 anchors +(5 pyramid levels × 9 anchors/cell), apply sigmoid to the logits, decode the box +regressions relative to the anchors, threshold on confidence and run NMS (IoU 0.6). + +## Usage + +### Python +```bash +python demo.py --model efficientdet-d0_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png --conf 0.4 +``` + +Or import directly: +```python +import cv2 + +net = cv2.dnn.readNet("efficientdet-d0_2026jul.onnx") +# see demo.py for the full anchor decode + NMS pipeline +``` + +### C++ +The C++ demo runs inference with OpenCV's DNN module. Adjust the OpenCV paths to your setup: +```bash +OCV=/path/to/opencv # OpenCV source tree +OCVBUILD=/path/to/opencv/build # OpenCV build directory (generated headers + libs) +g++ -std=c++17 demo.cpp -o demo \ + -I$OCV/include \ + -I$OCV/modules/core/include \ + -I$OCV/modules/dnn/include \ + -I$OCV/modules/imgproc/include \ + -I$OCV/modules/imgcodecs/include \ + -I$OCVBUILD \ + -L$OCVBUILD/lib -Wl,-rpath,$OCVBUILD/lib -lopencv_dnn -lopencv_imgcodecs -lopencv_imgproc -lopencv_core +./demo --model efficientdet-d0_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png +``` + +## Conversion +The ONNX model was exported from the frozen TensorFlow graph with tf2onnx (opset 18) +via [convert_to_onnx.py](./convert_to_onnx.py) — input `image_arrays:0`, outputs +`concat:0` and `concat_1:0`, input shape overridden to `[1, 512, 512, 3]`. Requires +`tensorflow`, `tf2onnx`, and `onnx`. + +```bash +python convert_to_onnx.py --pb ../pb/efficientdet-d0.pb +``` + +## License +See [LICENSE](./LICENSE) — released under the Apache License 2.0. diff --git a/efficientdet-d0/convert_to_onnx.py b/efficientdet-d0/convert_to_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..5fc9423d6238c292f8cac7a45382a5588ddfed0c --- /dev/null +++ b/efficientdet-d0/convert_to_onnx.py @@ -0,0 +1,41 @@ +import argparse +import datetime + +import onnx +import tensorflow as tf +import tf2onnx + + +def load_graph_def(pb_path): + with tf.io.gfile.GFile(pb_path, "rb") as f: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(f.read()) + return graph_def + + +def main(): + parser = argparse.ArgumentParser(description="Export efficientdet-d0.pb to ONNX") + parser.add_argument("--pb", default="../pb/efficientdet-d0.pb") + parser.add_argument("--opset", type=int, default=18) + args = parser.parse_args() + + graph_def = load_graph_def(args.pb) + + model_proto, _ = tf2onnx.convert.from_graph_def( + graph_def, + input_names=["image_arrays:0"], + output_names=["concat:0", "concat_1:0"], + opset=args.opset, + shape_override={"image_arrays:0": [1, 512, 512, 3]}, + ) + onnx.checker.check_model(model_proto) + + stamp = datetime.datetime.now().strftime("%Y%b").lower() + onnx_path = "efficientdet-d0_%s.onnx" % stamp + with open(onnx_path, "wb") as f: + f.write(model_proto.SerializeToString()) + print("wrote", onnx_path) + + +if __name__ == "__main__": + main() diff --git a/efficientdet-d0/demo.cpp b/efficientdet-d0/demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..68a3375f55f13f3cdee9e5d651087de3bc7e16a5 --- /dev/null +++ b/efficientdet-d0/demo.cpp @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace cv; + +static std::string argVal(int argc, char** argv, const std::string& key, const std::string& def) +{ + for (int i = 1; i + 1 < argc; ++i) + if (key == argv[i]) return argv[i + 1]; + return def; +} + +struct Det { float x1, y1, x2, y2, score; int cid; }; + +int main(int argc, char** argv) +{ + std::string model = argVal(argc, argv, "--model", "efficientdet-d0_2026jul.onnx"); + std::string image = argVal(argc, argv, "--image", "example_outputs/input_image.png"); + std::string output = argVal(argc, argv, "--output", "example_outputs/output_image.png"); + float conf = std::stof(argVal(argc, argv, "--conf", "0.4")); + + const int sz = 512; + + Mat img = imread(image); + if (img.empty()) { std::cerr << "could not read image: " << image << std::endl; return 1; } + + Mat rgb; + cvtColor(img, rgb, COLOR_BGR2RGB); + resize(rgb, rgb, Size(sz, sz)); + if (!rgb.isContinuous()) rgb = rgb.clone(); + + int blobShape[] = {1, sz, sz, 3}; + Mat blob(4, blobShape, CV_8U, rgb.data); + dnn::Net net = dnn::readNetFromONNX(model); + net.setInput(blob); + std::vector outs; + net.forward(outs, net.getUnconnectedOutLayersNames()); + + const float* boxp = nullptr; + const float* clsp = nullptr; + int n = 0, nc = 0; + for (size_t i = 0; i < outs.size(); ++i) + { + const Mat& o = outs[i]; + const float* p = (const float*)o.data; + int last = o.size[o.dims - 1]; + if (last == 4) { boxp = p; n = o.size[o.dims - 2]; } + else { clsp = p; nc = last; } + } + + std::vector> baseWH; + double asp[3][2] = {{1.0, 1.0}, {1.4, 0.7}, {0.7, 1.4}}; + for (int i = 0; i < 3; ++i) { + double s = std::pow(2.0, i / 3.0); + for (int a = 0; a < 3; ++a) + baseWH.push_back({(float)(32.0 * s * asp[a][0]), (float)(32.0 * s * asp[a][1])}); + } + std::vector acx, acy, aw, ah; + for (int lvl = 0; lvl < 5; ++lvl) { + int f = sz / (8 << lvl); + int step = 8 << lvl; + int m = 1 << lvl; + for (int y = 0; y < f; ++y) + for (int x = 0; x < f; ++x) { + float cx = (x + 0.5f) * step; + float cy = (y + 0.5f) * step; + for (auto& b : baseWH) { + acx.push_back(cx); acy.push_back(cy); + aw.push_back(b[0] * m); ah.push_back(b[1] * m); + } + } + } + + std::vector dets; + for (int a = 0; a < n; ++a) { + const float* bp = boxp + (size_t)a * 4; + float ycenter = bp[0] * ah[a] + acy[a]; + float xcenter = bp[1] * aw[a] + acx[a]; + float bhv = std::exp(bp[2]) * ah[a]; + float bwv = std::exp(bp[3]) * aw[a]; + const float* cp = clsp + (size_t)a * nc; + int best = 0; float bestLogit = cp[0]; + for (int c = 1; c < nc; ++c) if (cp[c] > bestLogit) { bestLogit = cp[c]; best = c; } + float score = 1.0f / (1.0f + std::exp(-bestLogit)); + if (score > conf) + dets.push_back({(xcenter - bwv / 2) / sz, (ycenter - bhv / 2) / sz, + (xcenter + bwv / 2) / sz, (ycenter + bhv / 2) / sz, score, best}); + } + + std::sort(dets.begin(), dets.end(), [](const Det& a, const Det& b) { return a.score > b.score; }); + std::vector removed(dets.size(), 0); + std::vector pick; + for (size_t i = 0; i < dets.size(); ++i) { + if (removed[i]) continue; + pick.push_back((int)i); + for (size_t j = i + 1; j < dets.size(); ++j) { + if (removed[j]) continue; + float xx1 = std::max(dets[i].x1, dets[j].x1); + float yy1 = std::max(dets[i].y1, dets[j].y1); + float xx2 = std::min(dets[i].x2, dets[j].x2); + float yy2 = std::min(dets[i].y2, dets[j].y2); + float inter = std::max(0.0f, xx2 - xx1) * std::max(0.0f, yy2 - yy1); + float ai = (dets[i].x2 - dets[i].x1) * (dets[i].y2 - dets[i].y1); + float aj = (dets[j].x2 - dets[j].x1) * (dets[j].y2 - dets[j].y1); + if (inter / (ai + aj - inter + 1e-9f) > 0.6f) removed[j] = 1; + } + } + + std::cout << "efficientdet-d0 " << pick.size() << " detections" << std::endl; + int w = img.cols, h = img.rows; + for (int idx : pick) { + const Det& d = dets[idx]; + std::cout << format("%d %.3f %.3f %.3f %.3f %.3f", d.cid, d.score, d.x1, d.y1, d.x2, d.y2) << std::endl; + rectangle(img, Point((int)(d.x1 * w), (int)(d.y1 * h)), Point((int)(d.x2 * w), (int)(d.y2 * h)), Scalar(0, 255, 0), 2); + putText(img, format("%d:%.2f", d.cid, d.score), Point((int)(d.x1 * w), (int)(d.y1 * h) - 5), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 1); + } + imwrite(output, img); + std::cout << "wrote " << output << std::endl; + return 0; +} diff --git a/efficientdet-d0/demo.py b/efficientdet-d0/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..3c0a600d524fcb744643e6e3ab84ffdb23894436 --- /dev/null +++ b/efficientdet-d0/demo.py @@ -0,0 +1,105 @@ +import argparse +import glob +import os + +import cv2 as cv +import numpy as np + +here = os.path.dirname(os.path.abspath(__file__)) +sz = 512 + + +def build_anchors(): + scales = [2.0 ** (i / 3.0) for i in range(3)] + aspects = [(1.0, 1.0), (1.4, 0.7), (0.7, 1.4)] + base = [] + for s in scales: + for aw, ah in aspects: + base.append((32.0 * s * aw, 32.0 * s * ah)) + anchors = [] + for lvl in range(5): + f = sz // (8 * 2 ** lvl) + step = 8 * 2 ** lvl + m = 2 ** lvl + for y in range(f): + for x in range(f): + cx = (x + 0.5) * step + cy = (y + 0.5) * step + for bw, bh in base: + anchors.append((cx, cy, bw * m, bh * m)) + return np.array(anchors, np.float32) + + +def main(): + parser = argparse.ArgumentParser(description="EfficientDet-D0 (ONNX) object detection demo") + parser.add_argument("--model", default=None) + parser.add_argument("--image", default=os.path.join(here, "example_outputs", "input_image.png")) + parser.add_argument("--output", default=os.path.join(here, "example_outputs", "output_image.png")) + parser.add_argument("--conf", type=float, default=0.4) + args = parser.parse_args() + + model = args.model + if model is None: + found = glob.glob(os.path.join(here, "*.onnx")) + if not found: + raise SystemExit("no onnx, run convert_to_onnx.py") + model = found[0] + + img = cv.imread(args.image) + if img is None: + raise SystemExit("could not read image: %s" % args.image) + + anchors = build_anchors() + acx, acy, aw, ah = anchors[:, 0], anchors[:, 1], anchors[:, 2], anchors[:, 3] + + net = cv.dnn.readNetFromONNX(model) + inp = cv.resize(cv.cvtColor(img, cv.COLOR_BGR2RGB), (sz, sz)) + net.setInput(inp[None].astype(np.uint8)) + res = net.forward(net.getUnconnectedOutLayersNames()) + box = next(a for a in res if a.shape[-1] == 4).reshape(-1, 4) + cls = next(a for a in res if a.shape[-1] != 4).reshape(box.shape[0], -1) + + ycenter = box[:, 0] * ah + acy + xcenter = box[:, 1] * aw + acx + bh = np.exp(box[:, 2]) * ah + bw = np.exp(box[:, 3]) * aw + boxes = np.stack([xcenter - bw / 2, ycenter - bh / 2, xcenter + bw / 2, ycenter + bh / 2], 1) / sz + + prob = 1.0 / (1.0 + np.exp(-cls)) + cid = prob.argmax(1) + scores = prob.max(1) + + keep = scores > args.conf + boxes = boxes[keep] + scores = scores[keep] + cid = cid[keep] + order = scores.argsort()[::-1] + pick = [] + while order.size: + i = order[0] + pick.append(i) + xx1 = np.maximum(boxes[i, 0], boxes[order[1:], 0]) + yy1 = np.maximum(boxes[i, 1], boxes[order[1:], 1]) + xx2 = np.minimum(boxes[i, 2], boxes[order[1:], 2]) + yy2 = np.minimum(boxes[i, 3], boxes[order[1:], 3]) + iw = np.maximum(0, xx2 - xx1) + ih = np.maximum(0, yy2 - yy1) + inter = iw * ih + ai = (boxes[i, 2] - boxes[i, 0]) * (boxes[i, 3] - boxes[i, 1]) + aj = (boxes[order[1:], 2] - boxes[order[1:], 0]) * (boxes[order[1:], 3] - boxes[order[1:], 1]) + iou = inter / (ai + aj - inter + 1e-9) + order = order[1:][iou <= 0.6] + + print("efficientdet-d0", len(pick), "detections") + h, w = img.shape[:2] + for i in pick: + x1, y1, x2, y2 = boxes[i] + print(int(cid[i]), round(float(scores[i]), 3), round(float(x1), 3), round(float(y1), 3), round(float(x2), 3), round(float(y2), 3)) + cv.rectangle(img, (int(x1 * w), int(y1 * h)), (int(x2 * w), int(y2 * h)), (0, 255, 0), 2) + cv.putText(img, "%d:%.2f" % (int(cid[i]), scores[i]), (int(x1 * w), int(y1 * h) - 5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) + cv.imwrite(args.output, img) + print("wrote", args.output) + + +if __name__ == "__main__": + main() diff --git a/efficientdet-d0/efficientdet-d0_2026jul.onnx b/efficientdet-d0/efficientdet-d0_2026jul.onnx new file mode 100644 index 0000000000000000000000000000000000000000..58f974d0aac25051d126f062134fb3edc10c3796 --- /dev/null +++ b/efficientdet-d0/efficientdet-d0_2026jul.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db344f69adf1c529e08a36bbaa1779d98b4b81621a2f046656fb966bdfdc6298 +size 15671001 diff --git a/efficientdet-d0/example_outputs/input_image.png b/efficientdet-d0/example_outputs/input_image.png new file mode 100644 index 0000000000000000000000000000000000000000..326ece9829794ab70eaf0b1e3feac0a762099689 --- /dev/null +++ b/efficientdet-d0/example_outputs/input_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61db162464c0770138c5136af0e999ce3b6556244cec257945427396480d9caf +size 414310 diff --git a/efficientdet-d0/example_outputs/output_image.png b/efficientdet-d0/example_outputs/output_image.png new file mode 100644 index 0000000000000000000000000000000000000000..3896e4cefbcb3c1c9e8c3b1f07f6bc7fb133a640 --- /dev/null +++ b/efficientdet-d0/example_outputs/output_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6edda1b62da08b28f04a227bc0054152b76b4dcbc04a50f856e6912fd8ea9b3a +size 330619 diff --git a/faster_rcnn_inception_v2_coco_2018_01_28/LICENSE b/faster_rcnn_inception_v2_coco_2018_01_28/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..4421e8e7b19941bd420a2723bb31d0abc15cd3d7 --- /dev/null +++ b/faster_rcnn_inception_v2_coco_2018_01_28/LICENSE @@ -0,0 +1,203 @@ +Copyright 2015 The TensorFlow Authors. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015, The TensorFlow Authors. + + 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. \ No newline at end of file diff --git a/faster_rcnn_inception_v2_coco_2018_01_28/README.md b/faster_rcnn_inception_v2_coco_2018_01_28/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d38446c01db2261da0581ef2d4dec39396ece56e --- /dev/null +++ b/faster_rcnn_inception_v2_coco_2018_01_28/README.md @@ -0,0 +1,49 @@ +# Faster-RCNN InceptionV2 (COCO) + +Object detection with the Faster-RCNN meta-architecture and an Inception v2 backbone, +trained on the COCO dataset. The model was originally distributed as a frozen TensorFlow +graph (`faster_rcnn_inception_v2_coco_2018_01_28.pb`) from the TensorFlow Object Detection +API and converted to ONNX for inference with OpenCV's DNN module. + +## Model Details +- **Architecture**: Faster-RCNN with an Inception v2 backbone +- **Input**: RGB image, uint8, NHWC layout (`image_tensor:0`, shape `[1, H, W, 3]`) +- **Output**: `detection_boxes:0` (normalized `[ymin, xmin, ymax, xmax]`), `detection_scores:0`, `detection_classes:0` (1-based COCO ids), `num_detections:0` +- **Framework**: ONNX (converted from the TensorFlow frozen graph via tf2onnx, opset 18) +- **Original weights**: http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz + +## Usage + +### Python +```bash +python demo.py --model faster_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png --conf 0.3 +``` + +### C++ +The C++ demo runs inference with OpenCV's DNN module. Adjust the OpenCV paths to your setup: +```bash +OCV=/path/to/opencv # OpenCV source tree +OCVBUILD=/path/to/opencv/build # OpenCV build directory (generated headers + libs) +g++ -std=c++17 demo.cpp -o demo \ + -I$OCV/include \ + -I$OCV/modules/core/include \ + -I$OCV/modules/dnn/include \ + -I$OCV/modules/imgproc/include \ + -I$OCV/modules/imgcodecs/include \ + -I$OCVBUILD \ + -L$OCVBUILD/lib -Wl,-rpath,$OCVBUILD/lib -lopencv_dnn -lopencv_imgcodecs -lopencv_imgproc -lopencv_core +./demo --model faster_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png +``` + +## Conversion +The ONNX model was exported from the frozen TensorFlow graph with tf2onnx (opset 18) +via [convert_to_onnx.py](./convert_to_onnx.py) — input `image_tensor:0`, outputs +`detection_boxes:0`, `detection_scores:0`, `detection_classes:0`, `num_detections:0`. +Requires `tensorflow`, `tf2onnx`, and `onnx`. + +```bash +python convert_to_onnx.py --pb ../pb/faster_rcnn_inception_v2_coco_2018_01_28.pb +``` + +## License +See [LICENSE](./LICENSE) — the model is released by the TensorFlow Authors under the Apache License 2.0. diff --git a/faster_rcnn_inception_v2_coco_2018_01_28/convert_to_onnx.py b/faster_rcnn_inception_v2_coco_2018_01_28/convert_to_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..7a068fa3176b9570edec5b4cfd5bb30ad93d15f0 --- /dev/null +++ b/faster_rcnn_inception_v2_coco_2018_01_28/convert_to_onnx.py @@ -0,0 +1,40 @@ +import argparse +import datetime + +import onnx +import tensorflow as tf +import tf2onnx + + +def load_graph_def(pb_path): + with tf.io.gfile.GFile(pb_path, "rb") as f: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(f.read()) + return graph_def + + +def main(): + parser = argparse.ArgumentParser(description="Export faster_rcnn_inception_v2_coco_2018_01_28.pb to ONNX") + parser.add_argument("--pb", default="../pb/faster_rcnn_inception_v2_coco_2018_01_28.pb") + parser.add_argument("--opset", type=int, default=18) + args = parser.parse_args() + + graph_def = load_graph_def(args.pb) + + model_proto, _ = tf2onnx.convert.from_graph_def( + graph_def, + input_names=["image_tensor:0"], + output_names=["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"], + opset=args.opset, + ) + onnx.checker.check_model(model_proto) + + stamp = datetime.datetime.now().strftime("%Y%b").lower() + onnx_path = "faster_rcnn_inception_v2_coco_2018_01_28_%s.onnx" % stamp + with open(onnx_path, "wb") as f: + f.write(model_proto.SerializeToString()) + print("wrote", onnx_path) + + +if __name__ == "__main__": + main() diff --git a/faster_rcnn_inception_v2_coco_2018_01_28/demo.cpp b/faster_rcnn_inception_v2_coco_2018_01_28/demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..549dd511c8479af904b1156ec2af5f8a8e9d782c --- /dev/null +++ b/faster_rcnn_inception_v2_coco_2018_01_28/demo.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static std::string argVal(int argc, char** argv, const std::string& key, const std::string& def) +{ + for (int i = 1; i + 1 < argc; ++i) + if (key == argv[i]) return argv[i + 1]; + return def; +} + +int main(int argc, char** argv) +{ + std::string model = argVal(argc, argv, "--model", "faster_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx"); + std::string image = argVal(argc, argv, "--image", "example_outputs/input_image.png"); + std::string output = argVal(argc, argv, "--output", "example_outputs/output_image.png"); + float conf = std::stof(argVal(argc, argv, "--conf", "0.3")); + + cv::Mat img = cv::imread(image); + if (img.empty()) + { + std::cerr << "could not read image: " << image << std::endl; + return 1; + } + + const int W = 800, H = 600; + cv::Mat rgb; + cv::cvtColor(img, rgb, cv::COLOR_BGR2RGB); + cv::resize(rgb, rgb, cv::Size(W, H)); + if (!rgb.isContinuous()) rgb = rgb.clone(); + + int blobShape[] = {1, H, W, 3}; + cv::Mat blob(4, blobShape, CV_8U, rgb.data); + cv::dnn::Net net = cv::dnn::readNetFromONNX(model, cv::dnn::ENGINE_ORT); + net.setInput(blob); + std::vector out_strs = {"detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"}; + std::vector outs; + net.forward(outs, out_strs); + + float* boxes = nullptr; + float* scores = nullptr; + float* classes = nullptr; + float* numd = nullptr; + for (size_t i = 0; i < out_strs.size(); ++i) + { + float* p = (float*)outs[i].data; + const std::string& n = out_strs[i]; + if (n.find("detection_boxes") != std::string::npos) boxes = p; + else if (n.find("detection_scores") != std::string::npos) scores = p; + else if (n.find("detection_classes") != std::string::npos) classes = p; + else if (n.find("num_detections") != std::string::npos) numd = p; + } + int nd = (int)numd[0]; + + int w = img.cols, h = img.rows; + std::vector kept; + for (int i = 0; i < nd; ++i) + if (scores[i] >= conf) kept.push_back(i); + + std::cout << "faster_rcnn_inception_v2_coco_2018_01_28 " << kept.size() << " detections" << std::endl; + for (int i : kept) + { + int cls = (int)classes[i] - 1; + float score = scores[i]; + float ymin = boxes[i * 4 + 0], xmin = boxes[i * 4 + 1]; + float ymax = boxes[i * 4 + 2], xmax = boxes[i * 4 + 3]; + cv::Point p1((int)(xmin * w), (int)(ymin * h)); + cv::Point p2((int)(xmax * w), (int)(ymax * h)); + cv::rectangle(img, p1, p2, cv::Scalar(0, 255, 0), 2); + cv::putText(img, cv::format("%d:%.2f", cls, score), cv::Point(p1.x, p1.y - 5), + cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 1); + std::cout << cls << " " << cv::format("%.3f %.3f %.3f %.3f %.3f", score, xmin, ymin, xmax, ymax) << std::endl; + } + + cv::imwrite(output, img); + std::cout << "wrote " << output << std::endl; + return 0; +} diff --git a/faster_rcnn_inception_v2_coco_2018_01_28/demo.py b/faster_rcnn_inception_v2_coco_2018_01_28/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..893a190e01899a36d3a65d55386f8283a91ad1b0 --- /dev/null +++ b/faster_rcnn_inception_v2_coco_2018_01_28/demo.py @@ -0,0 +1,51 @@ +import argparse +import glob +import os + +import cv2 as cv +import numpy as np + +here = os.path.dirname(os.path.abspath(__file__)) + + +def main(): + parser = argparse.ArgumentParser(description="Faster-RCNN InceptionV2 (COCO) ONNX detection demo") + parser.add_argument("--model", default=None) + parser.add_argument("--image", default=os.path.join(here, "example_outputs", "input_image.png")) + parser.add_argument("--output", default=os.path.join(here, "example_outputs", "output_image.png")) + parser.add_argument("--conf", type=float, default=0.3) + args = parser.parse_args() + + model = args.model or glob.glob(os.path.join(here, "*.onnx"))[0] + img = cv.imread(args.image) + if img is None: + raise SystemExit("could not read image: %s" % args.image) + + rgb = cv.resize(cv.cvtColor(img, cv.COLOR_BGR2RGB), (800, 600)) + net = cv.dnn.readNetFromONNX(model, cv.dnn.ENGINE_ORT) + onames = ["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"] + net.setInput(rgb[None].astype(np.uint8)) + res = net.forward(onames) + boxes = res[[i for i, n in enumerate(onames) if "detection_boxes" in n][0]].reshape(-1, 4) + scores = res[[i for i, n in enumerate(onames) if "detection_scores" in n][0]].reshape(-1) + classes = res[[i for i, n in enumerate(onames) if "detection_classes" in n][0]].reshape(-1) + nd = int(res[[i for i, n in enumerate(onames) if "num_detections" in n][0]].reshape(-1)[0]) + + out = np.zeros((nd, 7), np.float32) + out[:, 1] = classes[:nd] - 1 + out[:, 2] = scores[:nd] + out[:, 3:7] = boxes[:nd][:, [1, 0, 3, 2]] + + h, w = img.shape[:2] + kept = [row for row in out if row[2] >= args.conf] + print(os.path.basename(here), len(kept), "detections") + for row in kept: + cv.rectangle(img, (int(row[3] * w), int(row[4] * h)), (int(row[5] * w), int(row[6] * h)), (0, 255, 0), 2) + cv.putText(img, "%d:%.2f" % (int(row[1]), row[2]), (int(row[3] * w), int(row[4] * h) - 5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) + print(int(row[1]), round(float(row[2]), 3), round(float(row[3]), 3), round(float(row[4]), 3), round(float(row[5]), 3), round(float(row[6]), 3)) + cv.imwrite(args.output, img) + print("wrote", args.output) + + +if __name__ == "__main__": + main() diff --git a/faster_rcnn_inception_v2_coco_2018_01_28/example_outputs/input_image.png b/faster_rcnn_inception_v2_coco_2018_01_28/example_outputs/input_image.png new file mode 100644 index 0000000000000000000000000000000000000000..326ece9829794ab70eaf0b1e3feac0a762099689 --- /dev/null +++ b/faster_rcnn_inception_v2_coco_2018_01_28/example_outputs/input_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61db162464c0770138c5136af0e999ce3b6556244cec257945427396480d9caf +size 414310 diff --git a/faster_rcnn_inception_v2_coco_2018_01_28/example_outputs/output_image.png b/faster_rcnn_inception_v2_coco_2018_01_28/example_outputs/output_image.png new file mode 100644 index 0000000000000000000000000000000000000000..ad9513ad6240fdfba347889986ffc2761d1780b9 --- /dev/null +++ b/faster_rcnn_inception_v2_coco_2018_01_28/example_outputs/output_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef5519b779267068a419b329f67bab3b3529549ffbc5ffc26e79f154c4411a4d +size 323543 diff --git a/faster_rcnn_inception_v2_coco_2018_01_28/faster_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx b/faster_rcnn_inception_v2_coco_2018_01_28/faster_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx new file mode 100644 index 0000000000000000000000000000000000000000..d794f127217042b3a3fb5505d5c5741f42c00ff0 --- /dev/null +++ b/faster_rcnn_inception_v2_coco_2018_01_28/faster_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bcf541da5a58e9d8ab6c16e4c803ccc4e0da7dd62ad89311ae2a75c36b39835b +size 57016094 diff --git a/faster_rcnn_resnet50_coco_2018_01_28/LICENSE b/faster_rcnn_resnet50_coco_2018_01_28/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..4421e8e7b19941bd420a2723bb31d0abc15cd3d7 --- /dev/null +++ b/faster_rcnn_resnet50_coco_2018_01_28/LICENSE @@ -0,0 +1,203 @@ +Copyright 2015 The TensorFlow Authors. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015, The TensorFlow Authors. + + 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. \ No newline at end of file diff --git a/faster_rcnn_resnet50_coco_2018_01_28/README.md b/faster_rcnn_resnet50_coco_2018_01_28/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c0152dd30eb5371d58f7f0ad71499a7ce0b8d9b4 --- /dev/null +++ b/faster_rcnn_resnet50_coco_2018_01_28/README.md @@ -0,0 +1,49 @@ +# Faster-RCNN ResNet-50 (COCO) + +Object detection with the Faster-RCNN meta-architecture and a ResNet-50 backbone, +trained on the COCO dataset. The model was originally distributed as a frozen TensorFlow +graph (`faster_rcnn_resnet50_coco_2018_01_28.pb`) from the TensorFlow Object Detection +API and converted to ONNX for inference with OpenCV's DNN module. + +## Model Details +- **Architecture**: Faster-RCNN with a ResNet-50 backbone +- **Input**: RGB image, uint8, NHWC layout (`image_tensor:0`, shape `[1, H, W, 3]`) +- **Output**: `detection_boxes:0` (normalized `[ymin, xmin, ymax, xmax]`), `detection_scores:0`, `detection_classes:0` (1-based COCO ids), `num_detections:0` +- **Framework**: ONNX (converted from the TensorFlow frozen graph via tf2onnx, opset 18) +- **Original weights**: http://download.tensorflow.org/models/object_detection/faster_rcnn_resnet50_coco_2018_01_28.tar.gz + +## Usage + +### Python +```bash +python demo.py --model faster_rcnn_resnet50_coco_2018_01_28_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png --conf 0.3 +``` + +### C++ +The C++ demo runs inference with OpenCV's DNN module. Adjust the OpenCV paths to your setup: +```bash +OCV=/path/to/opencv # OpenCV source tree +OCVBUILD=/path/to/opencv/build # OpenCV build directory (generated headers + libs) +g++ -std=c++17 demo.cpp -o demo \ + -I$OCV/include \ + -I$OCV/modules/core/include \ + -I$OCV/modules/dnn/include \ + -I$OCV/modules/imgproc/include \ + -I$OCV/modules/imgcodecs/include \ + -I$OCVBUILD \ + -L$OCVBUILD/lib -Wl,-rpath,$OCVBUILD/lib -lopencv_dnn -lopencv_imgcodecs -lopencv_imgproc -lopencv_core +./demo --model faster_rcnn_resnet50_coco_2018_01_28_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png +``` + +## Conversion +The ONNX model was exported from the frozen TensorFlow graph with tf2onnx (opset 18) +via [convert_to_onnx.py](./convert_to_onnx.py) — input `image_tensor:0`, outputs +`detection_boxes:0`, `detection_scores:0`, `detection_classes:0`, `num_detections:0`. +Requires `tensorflow`, `tf2onnx`, and `onnx`. + +```bash +python convert_to_onnx.py --pb ../pb/faster_rcnn_resnet50_coco_2018_01_28.pb +``` + +## License +See [LICENSE](./LICENSE) — the model is released by the TensorFlow Authors under the Apache License 2.0. diff --git a/faster_rcnn_resnet50_coco_2018_01_28/convert_to_onnx.py b/faster_rcnn_resnet50_coco_2018_01_28/convert_to_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..e03a172b75d9ee116d6655433a37af9361c3f3d2 --- /dev/null +++ b/faster_rcnn_resnet50_coco_2018_01_28/convert_to_onnx.py @@ -0,0 +1,40 @@ +import argparse +import datetime + +import onnx +import tensorflow as tf +import tf2onnx + + +def load_graph_def(pb_path): + with tf.io.gfile.GFile(pb_path, "rb") as f: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(f.read()) + return graph_def + + +def main(): + parser = argparse.ArgumentParser(description="Export faster_rcnn_resnet50_coco_2018_01_28.pb to ONNX") + parser.add_argument("--pb", default="../pb/faster_rcnn_resnet50_coco_2018_01_28.pb") + parser.add_argument("--opset", type=int, default=18) + args = parser.parse_args() + + graph_def = load_graph_def(args.pb) + + model_proto, _ = tf2onnx.convert.from_graph_def( + graph_def, + input_names=["image_tensor:0"], + output_names=["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"], + opset=args.opset, + ) + onnx.checker.check_model(model_proto) + + stamp = datetime.datetime.now().strftime("%Y%b").lower() + onnx_path = "faster_rcnn_resnet50_coco_2018_01_28_%s.onnx" % stamp + with open(onnx_path, "wb") as f: + f.write(model_proto.SerializeToString()) + print("wrote", onnx_path) + + +if __name__ == "__main__": + main() diff --git a/faster_rcnn_resnet50_coco_2018_01_28/demo.cpp b/faster_rcnn_resnet50_coco_2018_01_28/demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cee9206ab323aa8caeef9e48e5a8576a1f531786 --- /dev/null +++ b/faster_rcnn_resnet50_coco_2018_01_28/demo.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static std::string argVal(int argc, char** argv, const std::string& key, const std::string& def) +{ + for (int i = 1; i + 1 < argc; ++i) + if (key == argv[i]) return argv[i + 1]; + return def; +} + +int main(int argc, char** argv) +{ + std::string model = argVal(argc, argv, "--model", "faster_rcnn_resnet50_coco_2018_01_28_2026jul.onnx"); + std::string image = argVal(argc, argv, "--image", "example_outputs/input_image.png"); + std::string output = argVal(argc, argv, "--output", "example_outputs/output_image.png"); + float conf = std::stof(argVal(argc, argv, "--conf", "0.3")); + + cv::Mat img = cv::imread(image); + if (img.empty()) + { + std::cerr << "could not read image: " << image << std::endl; + return 1; + } + + const int W = 800, H = 600; + cv::Mat rgb; + cv::cvtColor(img, rgb, cv::COLOR_BGR2RGB); + cv::resize(rgb, rgb, cv::Size(W, H)); + if (!rgb.isContinuous()) rgb = rgb.clone(); + + int blobShape[] = {1, H, W, 3}; + cv::Mat blob(4, blobShape, CV_8U, rgb.data); + cv::dnn::Net net = cv::dnn::readNetFromONNX(model, cv::dnn::ENGINE_ORT); + net.setInput(blob); + std::vector out_strs = {"detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"}; + std::vector outs; + net.forward(outs, out_strs); + + float* boxes = nullptr; + float* scores = nullptr; + float* classes = nullptr; + float* numd = nullptr; + for (size_t i = 0; i < out_strs.size(); ++i) + { + float* p = (float*)outs[i].data; + const std::string& n = out_strs[i]; + if (n.find("detection_boxes") != std::string::npos) boxes = p; + else if (n.find("detection_scores") != std::string::npos) scores = p; + else if (n.find("detection_classes") != std::string::npos) classes = p; + else if (n.find("num_detections") != std::string::npos) numd = p; + } + int nd = (int)numd[0]; + + int w = img.cols, h = img.rows; + std::vector kept; + for (int i = 0; i < nd; ++i) + if (scores[i] >= conf) kept.push_back(i); + + std::cout << "faster_rcnn_resnet50_coco_2018_01_28 " << kept.size() << " detections" << std::endl; + for (int i : kept) + { + int cls = (int)classes[i] - 1; + float score = scores[i]; + float ymin = boxes[i * 4 + 0], xmin = boxes[i * 4 + 1]; + float ymax = boxes[i * 4 + 2], xmax = boxes[i * 4 + 3]; + cv::Point p1((int)(xmin * w), (int)(ymin * h)); + cv::Point p2((int)(xmax * w), (int)(ymax * h)); + cv::rectangle(img, p1, p2, cv::Scalar(0, 255, 0), 2); + cv::putText(img, cv::format("%d:%.2f", cls, score), cv::Point(p1.x, p1.y - 5), + cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 1); + std::cout << cls << " " << cv::format("%.3f %.3f %.3f %.3f %.3f", score, xmin, ymin, xmax, ymax) << std::endl; + } + + cv::imwrite(output, img); + std::cout << "wrote " << output << std::endl; + return 0; +} diff --git a/faster_rcnn_resnet50_coco_2018_01_28/demo.py b/faster_rcnn_resnet50_coco_2018_01_28/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..3648d5933e465a2a2d807423b8bd9aaceab7c361 --- /dev/null +++ b/faster_rcnn_resnet50_coco_2018_01_28/demo.py @@ -0,0 +1,51 @@ +import argparse +import glob +import os + +import cv2 as cv +import numpy as np + +here = os.path.dirname(os.path.abspath(__file__)) + + +def main(): + parser = argparse.ArgumentParser(description="Faster-RCNN ResNet-50 (COCO) ONNX detection demo") + parser.add_argument("--model", default=None) + parser.add_argument("--image", default=os.path.join(here, "example_outputs", "input_image.png")) + parser.add_argument("--output", default=os.path.join(here, "example_outputs", "output_image.png")) + parser.add_argument("--conf", type=float, default=0.3) + args = parser.parse_args() + + model = args.model or glob.glob(os.path.join(here, "*.onnx"))[0] + img = cv.imread(args.image) + if img is None: + raise SystemExit("could not read image: %s" % args.image) + + rgb = cv.resize(cv.cvtColor(img, cv.COLOR_BGR2RGB), (800, 600)) + net = cv.dnn.readNetFromONNX(model, cv.dnn.ENGINE_ORT) + onames = ["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"] + net.setInput(rgb[None].astype(np.uint8)) + res = net.forward(onames) + boxes = res[[i for i, n in enumerate(onames) if "detection_boxes" in n][0]].reshape(-1, 4) + scores = res[[i for i, n in enumerate(onames) if "detection_scores" in n][0]].reshape(-1) + classes = res[[i for i, n in enumerate(onames) if "detection_classes" in n][0]].reshape(-1) + nd = int(res[[i for i, n in enumerate(onames) if "num_detections" in n][0]].reshape(-1)[0]) + + out = np.zeros((nd, 7), np.float32) + out[:, 1] = classes[:nd] - 1 + out[:, 2] = scores[:nd] + out[:, 3:7] = boxes[:nd][:, [1, 0, 3, 2]] + + h, w = img.shape[:2] + kept = [row for row in out if row[2] >= args.conf] + print(os.path.basename(here), len(kept), "detections") + for row in kept: + cv.rectangle(img, (int(row[3] * w), int(row[4] * h)), (int(row[5] * w), int(row[6] * h)), (0, 255, 0), 2) + cv.putText(img, "%d:%.2f" % (int(row[1]), row[2]), (int(row[3] * w), int(row[4] * h) - 5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) + print(int(row[1]), round(float(row[2]), 3), round(float(row[3]), 3), round(float(row[4]), 3), round(float(row[5]), 3), round(float(row[6]), 3)) + cv.imwrite(args.output, img) + print("wrote", args.output) + + +if __name__ == "__main__": + main() diff --git a/faster_rcnn_resnet50_coco_2018_01_28/example_outputs/input_image.png b/faster_rcnn_resnet50_coco_2018_01_28/example_outputs/input_image.png new file mode 100644 index 0000000000000000000000000000000000000000..326ece9829794ab70eaf0b1e3feac0a762099689 --- /dev/null +++ b/faster_rcnn_resnet50_coco_2018_01_28/example_outputs/input_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61db162464c0770138c5136af0e999ce3b6556244cec257945427396480d9caf +size 414310 diff --git a/faster_rcnn_resnet50_coco_2018_01_28/example_outputs/output_image.png b/faster_rcnn_resnet50_coco_2018_01_28/example_outputs/output_image.png new file mode 100644 index 0000000000000000000000000000000000000000..76e0171686a9e15bda4c23f971d80146628e658e --- /dev/null +++ b/faster_rcnn_resnet50_coco_2018_01_28/example_outputs/output_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1111a91eb2db29d6293ef1b0da18e2ca39c878ad709e10e1892ec4e028e9d4cc +size 328177 diff --git a/faster_rcnn_resnet50_coco_2018_01_28/faster_rcnn_resnet50_coco_2018_01_28_2026jul.onnx b/faster_rcnn_resnet50_coco_2018_01_28/faster_rcnn_resnet50_coco_2018_01_28_2026jul.onnx new file mode 100644 index 0000000000000000000000000000000000000000..56f77d974e05fa0d85053a2acd61547e01f23c86 --- /dev/null +++ b/faster_rcnn_resnet50_coco_2018_01_28/faster_rcnn_resnet50_coco_2018_01_28_2026jul.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:217613103b36eba4771087bdae63a7c04ed545e7850087f205990db7e5f18b88 +size 120414387 diff --git a/mask_rcnn_inception_v2_coco_2018_01_28/LICENSE b/mask_rcnn_inception_v2_coco_2018_01_28/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..4421e8e7b19941bd420a2723bb31d0abc15cd3d7 --- /dev/null +++ b/mask_rcnn_inception_v2_coco_2018_01_28/LICENSE @@ -0,0 +1,203 @@ +Copyright 2015 The TensorFlow Authors. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015, The TensorFlow Authors. + + 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. \ No newline at end of file diff --git a/mask_rcnn_inception_v2_coco_2018_01_28/README.md b/mask_rcnn_inception_v2_coco_2018_01_28/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f3f36e1123ad2ef769f059f2c9e4f04c7bfa7351 --- /dev/null +++ b/mask_rcnn_inception_v2_coco_2018_01_28/README.md @@ -0,0 +1,51 @@ +# Mask-RCNN Inception v2 COCO + +Instance segmentation with the Mask-RCNN Inception v2 network trained on the COCO dataset. +The model was originally distributed as a frozen TensorFlow graph +(`mask_rcnn_inception_v2_coco_2018_01_28.pb`) from the TensorFlow Object Detection API +and converted to ONNX for use with OpenCV's DNN module. + +## Model Details +- **Architecture**: Mask-RCNN with an Inception v2 backbone +- **Input**: RGB image, uint8, NHWC layout (`image_tensor:0`); the demo resizes to 800×800 +- **Output**: `num_detections:0`, `detection_boxes:0` (normalized `[ymin, xmin, ymax, xmax]`), + `detection_scores:0`, `detection_classes:0` (COCO ids, subtract 1 for a 0-based label), + and `detection_masks:0` (a 15×15 mask per detection, resized to its box) +- **Framework**: ONNX (converted from the TensorFlow frozen graph via tf2onnx, opset 18) +- **Original weights**: http://download.tensorflow.org/models/object_detection/mask_rcnn_inception_v2_coco_2018_01_28.tar.gz + +## Usage + +### Python +```bash +python demo.py --model mask_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png --conf 0.3 +``` + +### C++ +The C++ demo runs inference with OpenCV's DNN module. Adjust the OpenCV paths to your setup: +```bash +OCV=/path/to/opencv # OpenCV source tree +OCVBUILD=/path/to/opencv/build # OpenCV build directory (generated headers + libs) +g++ -std=c++17 demo.cpp -o demo \ + -I$OCV/include \ + -I$OCV/modules/core/include \ + -I$OCV/modules/dnn/include \ + -I$OCV/modules/imgproc/include \ + -I$OCV/modules/imgcodecs/include \ + -I$OCVBUILD \ + -L$OCVBUILD/lib -Wl,-rpath,$OCVBUILD/lib -lopencv_dnn -lopencv_imgcodecs -lopencv_imgproc -lopencv_core +./demo --model mask_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png +``` + +## Conversion +The ONNX model was exported from the frozen TensorFlow graph with tf2onnx (opset 18) +via [convert_to_onnx.py](./convert_to_onnx.py) — input `image_tensor:0`, outputs +`num_detections:0`, `detection_boxes:0`, `detection_scores:0`, `detection_classes:0`, +and `detection_masks:0`. Requires `tensorflow`, `tf2onnx`, and `onnx`. + +```bash +python convert_to_onnx.py --pb ../pb/mask_rcnn_inception_v2_coco_2018_01_28.pb +``` + +## License +See [LICENSE](./LICENSE) — the model is released by the TensorFlow Authors under the Apache License 2.0. diff --git a/mask_rcnn_inception_v2_coco_2018_01_28/convert_to_onnx.py b/mask_rcnn_inception_v2_coco_2018_01_28/convert_to_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..07eb1d0756c951feeeedc2030f195a074a0bdf00 --- /dev/null +++ b/mask_rcnn_inception_v2_coco_2018_01_28/convert_to_onnx.py @@ -0,0 +1,46 @@ +import argparse +import datetime + +import onnx +import tensorflow as tf +import tf2onnx + + +def load_graph_def(pb_path): + with tf.io.gfile.GFile(pb_path, "rb") as f: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(f.read()) + return graph_def + + +def main(): + parser = argparse.ArgumentParser(description="Export mask_rcnn_inception_v2_coco_2018_01_28.pb to ONNX") + parser.add_argument("--pb", default="../pb/mask_rcnn_inception_v2_coco_2018_01_28.pb") + parser.add_argument("--opset", type=int, default=18) + args = parser.parse_args() + + graph_def = load_graph_def(args.pb) + + model_proto, _ = tf2onnx.convert.from_graph_def( + graph_def, + input_names=["image_tensor:0"], + output_names=[ + "num_detections:0", + "detection_boxes:0", + "detection_scores:0", + "detection_classes:0", + "detection_masks:0", + ], + opset=args.opset, + ) + onnx.checker.check_model(model_proto) + + stamp = datetime.datetime.now().strftime("%Y%b").lower() + onnx_path = "mask_rcnn_inception_v2_coco_2018_01_28_%s.onnx" % stamp + with open(onnx_path, "wb") as f: + f.write(model_proto.SerializeToString()) + print("wrote", onnx_path) + + +if __name__ == "__main__": + main() diff --git a/mask_rcnn_inception_v2_coco_2018_01_28/demo.cpp b/mask_rcnn_inception_v2_coco_2018_01_28/demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49c7faf6da9c3ec57f06217e758783049f29dd12 --- /dev/null +++ b/mask_rcnn_inception_v2_coco_2018_01_28/demo.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static std::string argVal(int argc, char** argv, const std::string& key, const std::string& def) +{ + for (int i = 1; i + 1 < argc; ++i) + if (key == argv[i]) return argv[i + 1]; + return def; +} + +int main(int argc, char** argv) +{ + std::string model = argVal(argc, argv, "--model", "mask_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx"); + std::string image = argVal(argc, argv, "--image", "example_outputs/input_image.png"); + std::string output = argVal(argc, argv, "--output", "example_outputs/output_image.png"); + float conf = std::stof(argVal(argc, argv, "--conf", "0.3")); + + cv::Mat img = cv::imread(image); + if (img.empty()) + { + std::cerr << "could not read image: " << image << std::endl; + return 1; + } + + const int W = 800, H = 800; + cv::Mat rgb; + cv::cvtColor(img, rgb, cv::COLOR_BGR2RGB); + cv::resize(rgb, rgb, cv::Size(W, H)); + if (!rgb.isContinuous()) rgb = rgb.clone(); + + int blobShape[] = {1, H, W, 3}; + cv::Mat blob(4, blobShape, CV_8U, rgb.data); + cv::dnn::Net net = cv::dnn::readNetFromONNX(model, cv::dnn::ENGINE_ORT); + net.setInput(blob); + std::vector out_strs = {"num_detections:0", "detection_boxes:0", "detection_scores:0", "detection_classes:0", "detection_masks:0"}; + std::vector outs; + net.forward(outs, out_strs); + + float* boxes = nullptr; + float* scores = nullptr; + float* classes = nullptr; + float* numd = nullptr; + float* masks = nullptr; + for (size_t i = 0; i < out_strs.size(); ++i) + { + float* p = (float*)outs[i].data; + const std::string& n = out_strs[i]; + if (n.find("detection_boxes") != std::string::npos) boxes = p; + else if (n.find("detection_scores") != std::string::npos) scores = p; + else if (n.find("detection_classes") != std::string::npos) classes = p; + else if (n.find("num_detections") != std::string::npos) numd = p; + else if (n.find("detection_masks") != std::string::npos) masks = p; + } + int nd = (int)numd[0]; + + int w = img.cols, h = img.rows; + cv::Mat out = img.clone(); + std::vector kept; + for (int i = 0; i < nd; ++i) + if (scores[i] >= conf) kept.push_back(i); + + std::cout << "mask_rcnn_inception_v2_coco_2018_01_28 " << kept.size() << " detections" << std::endl; + for (int i : kept) + { + int cls = (int)classes[i] - 1; + float score = scores[i]; + float y1 = boxes[i * 4 + 0], x1 = boxes[i * 4 + 1]; + float y2 = boxes[i * 4 + 2], x2 = boxes[i * 4 + 3]; + int px1 = std::max(0, (int)(x1 * w)), py1 = std::max(0, (int)(y1 * h)); + int px2 = std::min(w, (int)(x2 * w)), py2 = std::min(h, (int)(y2 * h)); + + unsigned s = (unsigned)i * 2654435761u + 1u; + int col[3]; + for (int c = 0; c < 3; ++c) { s = s * 1664525u + 1013904223u; col[c] = 80 + (int)((s >> 8) % 176u); } + cv::Scalar color(col[0], col[1], col[2]); + + if (px2 > px1 && py2 > py1) + { + cv::Mat m15(15, 15, CV_32F, masks + (size_t)i * 225); + cv::Mat mr; + cv::resize(m15, mr, cv::Size(px2 - px1, py2 - py1)); + cv::Mat roi = out(cv::Rect(px1, py1, px2 - px1, py2 - py1)); + for (int y = 0; y < roi.rows; ++y) + { + cv::Vec3b* rp = roi.ptr(y); + const float* mp = mr.ptr(y); + for (int x = 0; x < roi.cols; ++x) + if (mp[x] > 0.5f) + for (int c = 0; c < 3; ++c) + rp[x][c] = (uchar)(0.5 * rp[x][c] + 0.5 * col[c]); + } + } + cv::rectangle(out, cv::Point(px1, py1), cv::Point(px2, py2), color, 2); + cv::putText(out, cv::format("%d:%.2f", cls, score), cv::Point(px1, py1 - 5), + cv::FONT_HERSHEY_SIMPLEX, 0.5, color, 1); + std::cout << cls << " " << cv::format("%.3f %.3f %.3f %.3f %.3f", score, x1, y1, x2, y2) << std::endl; + } + + cv::imwrite(output, out); + std::cout << "wrote " << output << std::endl; + return 0; +} diff --git a/mask_rcnn_inception_v2_coco_2018_01_28/demo.py b/mask_rcnn_inception_v2_coco_2018_01_28/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..2dd988a13e3fc374719a06e5204e2d066f8248ef --- /dev/null +++ b/mask_rcnn_inception_v2_coco_2018_01_28/demo.py @@ -0,0 +1,59 @@ +import argparse +import glob +import os + +import cv2 as cv +import numpy as np + +here = os.path.dirname(os.path.abspath(__file__)) + + +def main(): + parser = argparse.ArgumentParser(description="Mask-RCNN Inception v2 COCO (OpenCV DNN) detection + mask demo") + found = glob.glob(os.path.join(here, "*.onnx")) + parser.add_argument("--model", default=found[0] if found else None) + parser.add_argument("--image", default=os.path.join(here, "example_outputs", "input_image.png")) + parser.add_argument("--output", default=os.path.join(here, "example_outputs", "output_image.png")) + parser.add_argument("--conf", type=float, default=0.3) + args = parser.parse_args() + + img = cv.imread(args.image) + if img is None: + raise SystemExit("could not read image: %s" % args.image) + + net = cv.dnn.readNetFromONNX(args.model, cv.dnn.ENGINE_ORT) + rgb = cv.resize(cv.cvtColor(img, cv.COLOR_BGR2RGB), (800, 800)) + onames = ["num_detections:0", "detection_boxes:0", "detection_scores:0", "detection_classes:0", "detection_masks:0"] + net.setInput(rgb[None].astype(np.uint8)) + res = net.forward(onames) + boxes = res[[i for i, n in enumerate(onames) if "detection_boxes" in n][0]].reshape(-1, 4) + scores = res[[i for i, n in enumerate(onames) if "detection_scores" in n][0]].reshape(-1) + classes = res[[i for i, n in enumerate(onames) if "detection_classes" in n][0]].reshape(-1) + nd = int(res[[i for i, n in enumerate(onames) if "num_detections" in n][0]].reshape(-1)[0]) + masks = res[[i for i, n in enumerate(onames) if "detection_masks" in n][0]].reshape(-1, 15, 15) + + h, w = img.shape[:2] + out = img.copy() + kept = [i for i in range(nd) if scores[i] >= args.conf] + print("mask_rcnn_inception_v2_coco_2018_01_28", len(kept), "detections") + for i in kept: + cls = int(classes[i]) - 1 + score = float(scores[i]) + y1, x1, y2, x2 = boxes[i] + px1, py1 = max(0, int(x1 * w)), max(0, int(y1 * h)) + px2, py2 = min(w, int(x2 * w)), min(h, int(y2 * h)) + color = tuple(int(c) for c in np.random.default_rng(i).integers(80, 256, 3)) + if px2 > px1 and py2 > py1: + m = cv.resize(masks[i], (px2 - px1, py2 - py1)) > 0.5 + roi = out[py1:py2, px1:px2] + roi[m] = (0.5 * roi[m] + 0.5 * np.array(color)).astype(np.uint8) + cv.rectangle(out, (px1, py1), (px2, py2), color, 2) + cv.putText(out, "%d:%.2f" % (cls, score), (px1, py1 - 5), cv.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) + print(cls, round(score, 3), round(float(x1), 3), round(float(y1), 3), round(float(x2), 3), round(float(y2), 3)) + + cv.imwrite(args.output, out) + print("wrote", args.output) + + +if __name__ == "__main__": + main() diff --git a/mask_rcnn_inception_v2_coco_2018_01_28/example_outputs/input_image.png b/mask_rcnn_inception_v2_coco_2018_01_28/example_outputs/input_image.png new file mode 100644 index 0000000000000000000000000000000000000000..d892556078d2c17296761359f03ac8dca9b1be26 --- /dev/null +++ b/mask_rcnn_inception_v2_coco_2018_01_28/example_outputs/input_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d711ef10627f93def79c0c6ec2d0fc3da06cbb7e426d81fd2782538c3c549f52 +size 508341 diff --git a/mask_rcnn_inception_v2_coco_2018_01_28/example_outputs/output_image.png b/mask_rcnn_inception_v2_coco_2018_01_28/example_outputs/output_image.png new file mode 100644 index 0000000000000000000000000000000000000000..939b36c1f684166d9f3e4a7fbe127fd376a75397 --- /dev/null +++ b/mask_rcnn_inception_v2_coco_2018_01_28/example_outputs/output_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e1d9152370bbb8a15fcf7424f4a767edca0e9317d27f1f44346996eba6c887e +size 454347 diff --git a/mask_rcnn_inception_v2_coco_2018_01_28/mask_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx b/mask_rcnn_inception_v2_coco_2018_01_28/mask_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx new file mode 100644 index 0000000000000000000000000000000000000000..bfdf117cae2e198d4e034cd79e8c2b3b80b4ed3d --- /dev/null +++ b/mask_rcnn_inception_v2_coco_2018_01_28/mask_rcnn_inception_v2_coco_2018_01_28_2026jul.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27cdd89df33ab94f61fd278350f66996efec3437937b5679a311ba55e386690f +size 66728941 diff --git a/opencv_face_detector_uint8/LICENSE b/opencv_face_detector_uint8/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..1b021717fc0aba9870e2c60095334acb733ce922 --- /dev/null +++ b/opencv_face_detector_uint8/LICENSE @@ -0,0 +1,203 @@ +Copyright (c) OpenCV team and the opencv_3rdparty contributors. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) OpenCV team and the opencv_3rdparty contributors. + + 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. \ No newline at end of file diff --git a/opencv_face_detector_uint8/README.md b/opencv_face_detector_uint8/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5668500ae82d86eac0211aa75de5a2deebb0bc35 --- /dev/null +++ b/opencv_face_detector_uint8/README.md @@ -0,0 +1,67 @@ +# OpenCV SSD Face Detector (UINT8) + +Single-shot face detection with the OpenCV SSD ResNet-10 network. The model ships in the +OpenCV project as a quantized frozen TensorFlow graph (`opencv_face_detector_uint8.pb`) and is +converted here to ONNX for use with OpenCV's DNN module. Only the backbone is +exported — PriorBox generation, the confidence softmax, variance decode, score threshold and NMS +are run in host code (see `demo.py` / `demo.cpp`). + +## Model Details +- **Architecture**: SSD with a ResNet-10 backbone (face detector) +- **Input**: BGR image, 300×300, mean-subtracted by `[104, 177, 123]` (no scaling, no RGB swap), + NHWC layout (`data:0`, shape `[1, 300, 300, 3]`) +- **Output**: `mbox_loc` (`[1, 35568]`, box regressions) and `mbox_conf_flatten` + (`[1, 17784]`, 2-class face/background logits); PriorBox decode + softmax + NMS are done in the demo +- **Framework**: ONNX (converted from the TensorFlow frozen graph — uint8 weights with the + `Dequantize` nodes folded to float `Const` — via tf2onnx, opset 18) +- **Original weights**: https://github.com/opencv/opencv_3rdparty/raw/8033c2bc31b3256f0d461c919ecc01c2428ca03b/opencv_face_detector_uint8.pb + +The 6 SSD prior layers (min/max size, aspect ratios, step, feature-map size), the variances +`[0.1, 0.1, 0.2, 0.2]`, the default confidence threshold `0.4` and the NMS IoU `0.3` are all +defined in the demo scripts. + +## Usage + +### Python +```bash +python demo.py --model opencv_face_detector_uint8_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png --conf 0.4 +``` + +Or import directly: +```python +import cv2 + +net = cv2.dnn.readNet("opencv_face_detector_uint8_2026jul.onnx") +# see demo.py for the full PriorBox decode + softmax + NMS pipeline +``` + +### C++ +The C++ demo runs inference with OpenCV's DNN module. Adjust the OpenCV paths to your setup: +```bash +OCV=/path/to/opencv # OpenCV source tree +OCVBUILD=/path/to/opencv/build # OpenCV build directory (generated headers + libs) +g++ -std=c++17 demo.cpp -o demo \ + -I$OCV/include \ + -I$OCV/modules/core/include \ + -I$OCV/modules/dnn/include \ + -I$OCV/modules/imgproc/include \ + -I$OCV/modules/imgcodecs/include \ + -I$OCVBUILD \ + -L$OCVBUILD/lib -Wl,-rpath,$OCVBUILD/lib -lopencv_dnn -lopencv_imgcodecs -lopencv_imgproc -lopencv_core +./demo --model opencv_face_detector_uint8_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png +``` + +## Conversion +The ONNX model was exported from the frozen TensorFlow graph with tf2onnx (opset 18) via +[convert_to_onnx.py](./convert_to_onnx.py). The `.pb` stores its weights behind `Dequantize` +nodes, so the script first folds every `Dequantize` node to a float `Const` before conversion. +Inputs `data:0`, outputs `mbox_loc:0` and `mbox_conf_flatten:0`, input shape overridden to +`[1, 300, 300, 3]`. Requires `tensorflow`, `tf2onnx`, and `onnx`. + +```bash +python convert_to_onnx.py --pb ../pb/opencv_face_detector_uint8.pb +``` + +## License +See [LICENSE](./LICENSE) — this is the OpenCV face detector distributed via `opencv_3rdparty` +under the Apache License 2.0. diff --git a/opencv_face_detector_uint8/convert_to_onnx.py b/opencv_face_detector_uint8/convert_to_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..05edd72d7bdae1c4c96d3e0604379a7d3d00fbd7 --- /dev/null +++ b/opencv_face_detector_uint8/convert_to_onnx.py @@ -0,0 +1,73 @@ +import argparse +import datetime + +import numpy as np +import tensorflow as tf +import tf2onnx +import onnx +from tensorflow.python.framework import graph_util, tensor_util + + +def dequantize(graph_def, outputs): + dmin = {n.name: n.input[1] for n in graph_def.node if n.op == "Dequantize"} + folded = {} + with tf.Graph().as_default() as g: + tf.import_graph_def(graph_def, name="") + deq = list(dmin) + with tf.compat.v1.Session(graph=g) as sess: + for name in deq: + v = np.asarray(sess.run(g.get_tensor_by_name(name + ":0")), np.float32) + if not np.isfinite(v).all(): + mn = np.float32(sess.run(g.get_tensor_by_name(dmin[name] + ":0"))) + v = np.full(v.shape, mn, np.float32) + folded[name] = v + new = tf.compat.v1.GraphDef() + for n in graph_def.node: + if n.op == "Dequantize": + v = folded[n.name] + c = new.node.add() + c.op = "Const" + c.name = n.name + c.attr["dtype"].type = tf.float32.as_datatype_enum + c.attr["value"].tensor.CopyFrom(tensor_util.make_tensor_proto(v, tf.float32, v.shape)) + else: + new.node.add().CopyFrom(n) + return graph_util.extract_sub_graph(new, [o.split(":")[0] for o in outputs]) + + +def load_graph_def(pb_path): + with tf.io.gfile.GFile(pb_path, "rb") as f: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(f.read()) + return graph_def + + +def main(): + parser = argparse.ArgumentParser(description="Export opencv_face_detector_uint8.pb (backbone) to ONNX") + parser.add_argument("--pb", default="../pb/opencv_face_detector_uint8.pb") + parser.add_argument("--opset", type=int, default=18) + args = parser.parse_args() + + output_names = ["mbox_loc:0", "mbox_conf_flatten:0"] + + graph_def = load_graph_def(args.pb) + graph_def = dequantize(graph_def, output_names) + + model_proto, _ = tf2onnx.convert.from_graph_def( + graph_def, + input_names=["data:0"], + output_names=output_names, + opset=args.opset, + shape_override={"data:0": [1, 300, 300, 3]}, + ) + onnx.checker.check_model(model_proto) + + stamp = datetime.datetime.now().strftime("%Y%b").lower() + onnx_path = "opencv_face_detector_uint8_%s.onnx" % stamp + with open(onnx_path, "wb") as f: + f.write(model_proto.SerializeToString()) + print("wrote", onnx_path) + + +if __name__ == "__main__": + main() diff --git a/opencv_face_detector_uint8/demo.cpp b/opencv_face_detector_uint8/demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b5d20538e8830c82fefa6c069d7cad0fdf70d68 --- /dev/null +++ b/opencv_face_detector_uint8/demo.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace cv; + +static std::string argVal(int argc, char** argv, const std::string& key, const std::string& def) +{ + for (int i = 1; i + 1 < argc; ++i) + if (key == argv[i]) return argv[i + 1]; + return def; +} + +struct Layer { float mn, mx; std::vector ars; int step, fm; }; + +int main(int argc, char** argv) +{ + std::string model = argVal(argc, argv, "--model", "opencv_face_detector_uint8_2026jul.onnx"); + std::string image = argVal(argc, argv, "--image", "example_outputs/input_image.png"); + std::string output = argVal(argc, argv, "--output", "example_outputs/output_image.png"); + float thr = std::stof(argVal(argc, argv, "--conf", "0.4")); + + const int sz = 300; + Mat img = imread(image); + if (img.empty()) + { + std::cerr << "could not read image: " << image << std::endl; + return 1; + } + + Mat inp; + resize(img, inp, Size(sz, sz)); + inp.convertTo(inp, CV_32F); + subtract(inp, Scalar(104, 177, 123), inp); + if (!inp.isContinuous()) inp = inp.clone(); + + int blobShape[] = {1, sz, sz, 3}; + Mat blob(4, blobShape, CV_32F, inp.data); + dnn::Net net = dnn::readNetFromONNX(model); + net.setInput(blob); + std::vector outs; + net.forward(outs, net.getUnconnectedOutLayersNames()); + + const float* loc = nullptr; + const float* conf = nullptr; + for (size_t i = 0; i < outs.size(); ++i) + { + const Mat& o = outs[i]; + size_t tot = o.total(); + const float* p = (const float*)o.data; + if (tot == 35568) loc = p; + else if (tot == 17784) conf = p; + } + + std::vector layers = { + {30, 60, {2}, 8, 38}, + {60, 111, {2, 3}, 16, 19}, + {111, 162, {2, 3}, 32, 10}, + {162, 213, {2, 3}, 64, 5}, + {213, 264, {2}, 100, 5}, + {264, 315, {2}, 300, 5}, + }; + std::vector priors; + for (const Layer& L : layers) + { + std::vector ratios = {1.0f}; + for (int a : L.ars) { ratios.push_back((float)a); ratios.push_back(1.0f / a); } + for (int y = 0; y < L.fm; ++y) + for (int x = 0; x < L.fm; ++x) + { + float cx = (x + 0.5f) * L.step; + float cy = (y + 0.5f) * L.step; + std::vector boxes = {{L.mn, L.mn}, {std::sqrt(L.mn * L.mx), std::sqrt(L.mn * L.mx)}}; + for (size_t k = 1; k < ratios.size(); ++k) + { + float a = ratios[k]; + boxes.push_back({L.mn * std::sqrt(a), L.mn / std::sqrt(a)}); + } + for (const Vec2f& b : boxes) + priors.push_back({cx, cy, b[0], b[1]}); + } + } + + const float var[4] = {0.1f, 0.1f, 0.2f, 0.2f}; + int n = (int)priors.size(); + std::vector boxes; + std::vector scores; + for (int i = 0; i < n; ++i) + { + float c0 = conf[i * 2], c1 = conf[i * 2 + 1]; + float m = std::max(c0, c1); + float e0 = std::exp(c0 - m), e1 = std::exp(c1 - m); + float s = e1 / (e0 + e1); + if (s <= thr) continue; + float pcx = priors[i][0] / sz, pcy = priors[i][1] / sz; + float pw = priors[i][2] / sz, ph = priors[i][3] / sz; + float cx = pcx + loc[i * 4] * var[0] * pw; + float cy = pcy + loc[i * 4 + 1] * var[1] * ph; + float bw = pw * std::exp(loc[i * 4 + 2] * var[2]); + float bh = ph * std::exp(loc[i * 4 + 3] * var[3]); + boxes.push_back(Rect2f(cx - bw / 2, cy - bh / 2, bw, bh)); + scores.push_back(s); + } + + std::vector order(scores.size()); + for (size_t i = 0; i < order.size(); ++i) order[i] = (int)i; + std::sort(order.begin(), order.end(), [&](int a, int b){ return scores[a] > scores[b]; }); + std::vector removed(order.size(), 0); + std::vector pick; + for (size_t oi = 0; oi < order.size(); ++oi) + { + if (removed[oi]) continue; + int i = order[oi]; + pick.push_back(i); + for (size_t oj = oi + 1; oj < order.size(); ++oj) + { + if (removed[oj]) continue; + int j = order[oj]; + const Rect2f& a = boxes[i]; + const Rect2f& b = boxes[j]; + float xx1 = std::max(a.x, b.x), yy1 = std::max(a.y, b.y); + float xx2 = std::min(a.x + a.width, b.x + b.width); + float yy2 = std::min(a.y + a.height, b.y + b.height); + float inter = std::max(0.f, xx2 - xx1) * std::max(0.f, yy2 - yy1); + float iou = inter / (a.area() + b.area() - inter + 1e-9f); + if (iou > 0.3f) removed[oj] = 1; + } + } + + int W = img.cols, H = img.rows; + for (int i : pick) + { + const Rect2f& b = boxes[i]; + rectangle(img, Point(int(b.x * W), int(b.y * H)), + Point(int((b.x + b.width) * W), int((b.y + b.height) * H)), Scalar(0, 255, 0), 2); + } + imwrite(output, img); + std::cout << "opencv_face_detector_uint8 " << pick.size() << " faces" << std::endl; + return 0; +} diff --git a/opencv_face_detector_uint8/demo.py b/opencv_face_detector_uint8/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..4b1c44b9c037345ec78be800516ce70a6f2fab0b --- /dev/null +++ b/opencv_face_detector_uint8/demo.py @@ -0,0 +1,109 @@ +import argparse +import glob +import os + +import cv2 as cv +import numpy as np + +here = os.path.dirname(os.path.abspath(__file__)) + +sz = 300 +layers = [ + (30, 60, [2], 8, 38), + (60, 111, [2, 3], 16, 19), + (111, 162, [2, 3], 32, 10), + (162, 213, [2, 3], 64, 5), + (213, 264, [2], 100, 5), + (264, 315, [2], 300, 5), +] +var = [0.1, 0.1, 0.2, 0.2] + + +def build_priors(): + p = [] + for mn, mx, ars, step, fm in layers: + ratios = [1.0] + for a in ars: + ratios += [a, 1.0 / a] + for y in range(fm): + for x in range(fm): + cx = (x + 0.5) * step + cy = (y + 0.5) * step + boxes = [(mn, mn), ((mn * mx) ** 0.5, (mn * mx) ** 0.5)] + for a in ratios[1:]: + boxes.append((mn * a ** 0.5, mn / a ** 0.5)) + for bw, bh in boxes: + p.append([cx, cy, bw, bh]) + return np.array(p, np.float32) + + +def default_model(): + files = [f for f in glob.glob(os.path.join(here, "*.onnx")) if "known_good" not in os.path.basename(f)] + return files[0] if files else os.path.join(here, "opencv_face_detector_uint8.onnx") + + +def main(): + parser = argparse.ArgumentParser(description="OpenCV SSD face detector (ONNX) demo") + parser.add_argument("--model", default=default_model()) + parser.add_argument("--image", default=os.path.join(here, "example_outputs", "input_image.png")) + parser.add_argument("--output", default=os.path.join(here, "example_outputs", "output_image.png")) + parser.add_argument("--conf", type=float, default=0.4) + args = parser.parse_args() + + img = cv.imread(args.image) + if img is None: + raise SystemExit("could not read image: %s" % args.image) + + inp = cv.resize(img, (sz, sz)).astype(np.float32) - np.array([104.0, 177.0, 123.0], np.float32) + + net = cv.dnn.readNetFromONNX(args.model) + onames = net.getUnconnectedOutLayersNames() + net.setInput(inp[None]) + res = net.forward(onames) + loc = res[[i for i, n in enumerate(onames) if "mbox_loc" in n][0]].reshape(-1, 4) + conf = res[[i for i, n in enumerate(onames) if "mbox_conf" in n][0]].reshape(-1, 2) + + priors = build_priors() + pcx = priors[:, 0] / sz + pcy = priors[:, 1] / sz + pw = priors[:, 2] / sz + ph = priors[:, 3] / sz + + e = np.exp(conf - conf.max(1, keepdims=True)) + sm = e / e.sum(1, keepdims=True) + scores = sm[:, 1] + + cx = pcx + loc[:, 0] * var[0] * pw + cy = pcy + loc[:, 1] * var[1] * ph + bw = pw * np.exp(loc[:, 2] * var[2]) + bh = ph * np.exp(loc[:, 3] * var[3]) + boxes = np.stack([cx - bw / 2, cy - bh / 2, cx + bw / 2, cy + bh / 2], 1) + + keep = scores > args.conf + boxes = boxes[keep] + scores = scores[keep] + order = scores.argsort()[::-1] + pick = [] + while order.size: + i = order[0] + pick.append(i) + xx1 = np.maximum(boxes[i, 0], boxes[order[1:], 0]) + yy1 = np.maximum(boxes[i, 1], boxes[order[1:], 1]) + xx2 = np.minimum(boxes[i, 2], boxes[order[1:], 2]) + yy2 = np.minimum(boxes[i, 3], boxes[order[1:], 3]) + inter = np.maximum(0, xx2 - xx1) * np.maximum(0, yy2 - yy1) + ai = (boxes[i, 2] - boxes[i, 0]) * (boxes[i, 3] - boxes[i, 1]) + aj = (boxes[order[1:], 2] - boxes[order[1:], 0]) * (boxes[order[1:], 3] - boxes[order[1:], 1]) + iou = inter / (ai + aj - inter + 1e-9) + order = order[1:][iou <= 0.3] + + h, w = img.shape[:2] + for i in pick: + x1, y1, x2, y2 = boxes[i] + cv.rectangle(img, (int(x1 * w), int(y1 * h)), (int(x2 * w), int(y2 * h)), (0, 255, 0), 2) + cv.imwrite(args.output, img) + print("opencv_face_detector_uint8", len(pick), "faces") + + +if __name__ == "__main__": + main() diff --git a/opencv_face_detector_uint8/example_outputs/input_image.png b/opencv_face_detector_uint8/example_outputs/input_image.png new file mode 100644 index 0000000000000000000000000000000000000000..b4a10d783f902fadc45dc36d27d643b8a10d16a1 --- /dev/null +++ b/opencv_face_detector_uint8/example_outputs/input_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c232b8830623d924c518183cd005f7ff7d7b735e19c1cd26a2a59613d452a3d4 +size 198308 diff --git a/opencv_face_detector_uint8/example_outputs/output_image.png b/opencv_face_detector_uint8/example_outputs/output_image.png new file mode 100644 index 0000000000000000000000000000000000000000..643238827b09ca7f4a270456bf9dd37407841501 --- /dev/null +++ b/opencv_face_detector_uint8/example_outputs/output_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15a6e1a27e04020e9cbce7a26e7c2d8752cc74157b179bfe3636825688c13d4e +size 440053 diff --git a/opencv_face_detector_uint8/opencv_face_detector_uint8_2026jul.onnx b/opencv_face_detector_uint8/opencv_face_detector_uint8_2026jul.onnx new file mode 100644 index 0000000000000000000000000000000000000000..41a621f3887d48dc697004f78e890ec535e1be3b --- /dev/null +++ b/opencv_face_detector_uint8/opencv_face_detector_uint8_2026jul.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5b1efe9c4e792a010ac36248d92b64c3d94f9bdec764951d1e8684d919b1e40 +size 10671719 diff --git a/ssd_inception_v2_coco_2017_11_17/LICENSE b/ssd_inception_v2_coco_2017_11_17/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..12736ccd678d7be692590dcccf004a26233141df --- /dev/null +++ b/ssd_inception_v2_coco_2017_11_17/LICENSE @@ -0,0 +1,212 @@ +Copyright 2022 Google LLC. All rights reserved. + +All files in the following folders: +/community +/official +/orbit +/research +/tensorflow_models + +Are licensed as follows: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016, The Authors. + + 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. diff --git a/ssd_inception_v2_coco_2017_11_17/README.md b/ssd_inception_v2_coco_2017_11_17/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0e43eccedb682379d87e60ca31281fdc32290363 --- /dev/null +++ b/ssd_inception_v2_coco_2017_11_17/README.md @@ -0,0 +1,48 @@ +# SSD Inception v2 COCO + +Object detection with a Single Shot MultiBox Detector (SSD) built on an Inception v2 backbone, +trained on the COCO dataset. The model was originally distributed as a frozen TensorFlow graph +(`ssd_inception_v2_coco_2017_11_17.pb`) and converted to ONNX for use with OpenCV's DNN module. + +## Model Details +- **Architecture**: SSD (Single Shot MultiBox Detector) with Inception v2 backbone +- **Input**: RGB image, 300×300, raw uint8, NHWC layout (`image_tensor:0`, shape `[1, 300, 300, 3]`) +- **Output**: `detection_boxes:0` (normalized `ymin, xmin, ymax, xmax`), `detection_scores:0`, `detection_classes:0` (COCO class ids), `num_detections:0` +- **Framework**: ONNX (converted from the TensorFlow frozen graph via tf2onnx, opset 18) +- **Original weights**: http://download.tensorflow.org/models/object_detection/ssd_inception_v2_coco_2017_11_17.tar.gz + +## Usage + +### Python +```bash +python demo.py --model ssd_inception_v2_coco_2017_11_17_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png --conf 0.3 +``` + +### C++ +The C++ demo runs inference with OpenCV's DNN module. Adjust the OpenCV paths to your setup: +```bash +OCV=/path/to/opencv # OpenCV source tree +OCVBUILD=/path/to/opencv/build # OpenCV build directory (generated headers + libs) +g++ -std=c++17 demo.cpp -o demo \ + -I$OCV/include \ + -I$OCV/modules/core/include \ + -I$OCV/modules/dnn/include \ + -I$OCV/modules/imgproc/include \ + -I$OCV/modules/imgcodecs/include \ + -I$OCVBUILD \ + -L$OCVBUILD/lib -Wl,-rpath,$OCVBUILD/lib -lopencv_dnn -lopencv_imgcodecs -lopencv_imgproc -lopencv_core +./demo --model ssd_inception_v2_coco_2017_11_17_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png +``` + +## Conversion +The ONNX model was exported from the frozen TensorFlow graph with tf2onnx (opset 18) +via [convert_to_onnx.py](./convert_to_onnx.py) — input `image_tensor:0`, outputs +`detection_boxes:0`, `detection_scores:0`, `detection_classes:0`, `num_detections:0`. +Requires `tensorflow`, `tf2onnx`, and `onnx`. + +```bash +python convert_to_onnx.py --pb ../pb/ssd_inception_v2_coco_2017_11_17.pb +``` + +## License +See [LICENSE](./LICENSE) — the model is released by the TensorFlow Authors under the Apache License 2.0. diff --git a/ssd_inception_v2_coco_2017_11_17/convert_to_onnx.py b/ssd_inception_v2_coco_2017_11_17/convert_to_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..b5ec338d32bf45c1f6b450ab4de7c16361843997 --- /dev/null +++ b/ssd_inception_v2_coco_2017_11_17/convert_to_onnx.py @@ -0,0 +1,40 @@ +import argparse +import datetime + +import onnx +import tensorflow as tf +import tf2onnx + + +def load_graph_def(pb_path): + with tf.io.gfile.GFile(pb_path, "rb") as f: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(f.read()) + return graph_def + + +def main(): + parser = argparse.ArgumentParser(description="Export ssd_inception_v2_coco_2017_11_17.pb to ONNX") + parser.add_argument("--pb", default="../pb/ssd_inception_v2_coco_2017_11_17.pb") + parser.add_argument("--opset", type=int, default=18) + args = parser.parse_args() + + graph_def = load_graph_def(args.pb) + + model_proto, _ = tf2onnx.convert.from_graph_def( + graph_def, + input_names=["image_tensor:0"], + output_names=["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"], + opset=args.opset, + ) + onnx.checker.check_model(model_proto) + + stamp = datetime.datetime.now().strftime("%Y%b").lower() + onnx_path = "ssd_inception_v2_coco_2017_11_17_%s.onnx" % stamp + with open(onnx_path, "wb") as f: + f.write(model_proto.SerializeToString()) + print("wrote", onnx_path) + + +if __name__ == "__main__": + main() diff --git a/ssd_inception_v2_coco_2017_11_17/demo.cpp b/ssd_inception_v2_coco_2017_11_17/demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..98bd1b422b005f9cc3eef3d9862b2fde46f3e412 --- /dev/null +++ b/ssd_inception_v2_coco_2017_11_17/demo.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace cv; + +static std::string argVal(int argc, char** argv, const std::string& key, const std::string& def) +{ + for (int i = 1; i + 1 < argc; ++i) + if (key == argv[i]) return argv[i + 1]; + return def; +} + +int main(int argc, char** argv) +{ + std::string model = argVal(argc, argv, "--model", "ssd_inception_v2_coco_2017_11_17_2026jul.onnx"); + std::string image = argVal(argc, argv, "--image", "example_outputs/input_image.png"); + std::string output = argVal(argc, argv, "--output", "example_outputs/output_image.png"); + float conf = std::stof(argVal(argc, argv, "--conf", "0.3")); + + Mat img = imread(image); + if (img.empty()) + { + std::cerr << "could not read image: " << image << std::endl; + return 1; + } + + Mat rgb; + cvtColor(img, rgb, COLOR_BGR2RGB); + resize(rgb, rgb, Size(300, 300)); + if (!rgb.isContinuous()) rgb = rgb.clone(); + + int blobShape[] = {1, 300, 300, 3}; + Mat blob(4, blobShape, CV_8U, rgb.data); + dnn::Net net = dnn::readNetFromONNX(model, dnn::ENGINE_ORT); + net.setInput(blob); + std::vector out_str = {"detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"}; + std::vector outs; + net.forward(outs, out_str); + + const float *boxes = 0, *scores = 0, *classes = 0, *num = 0; + for (size_t i = 0; i < out_str.size(); ++i) + { + const std::string& n = out_str[i]; + if (n.find("detection_boxes") != std::string::npos) boxes = (const float*)outs[i].data; + else if (n.find("detection_scores") != std::string::npos) scores = (const float*)outs[i].data; + else if (n.find("detection_classes") != std::string::npos) classes = (const float*)outs[i].data; + else if (n.find("num_detections") != std::string::npos) num = (const float*)outs[i].data; + } + if (!boxes || !scores || !classes || !num) + { + std::cerr << "missing expected output tensors" << std::endl; + return 1; + } + + int nd = (int)num[0]; + int h = img.rows, w = img.cols; + Mat out = img.clone(); + std::vector lines; + for (int k = 0; k < nd; ++k) + { + if (scores[k] < conf) continue; + float ymin = boxes[k * 4 + 0], xmin = boxes[k * 4 + 1]; + float ymax = boxes[k * 4 + 2], xmax = boxes[k * 4 + 3]; + int cls = (int)classes[k]; + rectangle(out, Point(int(xmin * w), int(ymin * h)), Point(int(xmax * w), int(ymax * h)), Scalar(0, 255, 0), 2); + putText(out, format("%d:%.2f", cls, scores[k]), Point(int(xmin * w), int(ymin * h) - 5), + FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 1); + lines.push_back(format("%d %.3f %.3f %.3f %.3f %.3f", cls, scores[k], xmin, ymin, xmax, ymax)); + } + + imwrite(output, out); + std::cout << "ssd_inception_v2_coco_2017_11_17 " << lines.size() << " detections" << std::endl; + for (const auto& l : lines) std::cout << l << std::endl; + return 0; +} diff --git a/ssd_inception_v2_coco_2017_11_17/demo.py b/ssd_inception_v2_coco_2017_11_17/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..a8a42a785e5fd0c95e7c19dd726c1bf57432ec10 --- /dev/null +++ b/ssd_inception_v2_coco_2017_11_17/demo.py @@ -0,0 +1,52 @@ +import argparse +import glob +import os + +import cv2 as cv +import numpy as np + +here = os.path.dirname(os.path.abspath(__file__)) + + +def main(): + parser = argparse.ArgumentParser(description="SSD Inception v2 COCO (ONNX) object detection demo") + parser.add_argument("--model", default=(glob.glob(os.path.join(here, "*.onnx")) or [""])[0]) + parser.add_argument("--image", default=os.path.join(here, "example_outputs", "input_image.png")) + parser.add_argument("--output", default=os.path.join(here, "example_outputs", "output_image.png")) + parser.add_argument("--conf", type=float, default=0.3) + args = parser.parse_args() + + img = cv.imread(args.image) + if img is None: + raise SystemExit("could not read image: %s" % args.image) + + rgb = cv.resize(cv.cvtColor(img, cv.COLOR_BGR2RGB), (300, 300)) + + net = cv.dnn.readNetFromONNX(args.model, cv.dnn.ENGINE_ORT) + onames = ["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"] + net.setInput(rgb[None].astype(np.uint8)) + res = net.forward(onames) + boxes = res[[i for i, n in enumerate(onames) if "detection_boxes" in n][0]].reshape(-1, 4) + scores = res[[i for i, n in enumerate(onames) if "detection_scores" in n][0]].reshape(-1) + classes = res[[i for i, n in enumerate(onames) if "detection_classes" in n][0]].reshape(-1) + nd = int(res[[i for i, n in enumerate(onames) if "num_detections" in n][0]].reshape(-1)[0]) + + h, w = img.shape[:2] + out = img.copy() + kept = [] + for k in range(nd): + if scores[k] < args.conf: + continue + ymin, xmin, ymax, xmax = boxes[k] + kept.append((int(classes[k]), float(scores[k]), float(xmin), float(ymin), float(xmax), float(ymax))) + cv.rectangle(out, (int(xmin * w), int(ymin * h)), (int(xmax * w), int(ymax * h)), (0, 255, 0), 2) + cv.putText(out, "%d:%.2f" % (int(classes[k]), scores[k]), (int(xmin * w), int(ymin * h) - 5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) + + cv.imwrite(args.output, out) + print("ssd_inception_v2_coco_2017_11_17", len(kept), "detections") + for c, s, xmin, ymin, xmax, ymax in kept: + print(c, round(s, 3), round(xmin, 3), round(ymin, 3), round(xmax, 3), round(ymax, 3)) + + +if __name__ == "__main__": + main() diff --git a/ssd_inception_v2_coco_2017_11_17/example_outputs/input_image.png b/ssd_inception_v2_coco_2017_11_17/example_outputs/input_image.png new file mode 100644 index 0000000000000000000000000000000000000000..d892556078d2c17296761359f03ac8dca9b1be26 --- /dev/null +++ b/ssd_inception_v2_coco_2017_11_17/example_outputs/input_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d711ef10627f93def79c0c6ec2d0fc3da06cbb7e426d81fd2782538c3c549f52 +size 508341 diff --git a/ssd_inception_v2_coco_2017_11_17/example_outputs/output_image.png b/ssd_inception_v2_coco_2017_11_17/example_outputs/output_image.png new file mode 100644 index 0000000000000000000000000000000000000000..707ecffb6fb96d56b33a52c11ac420dc840f3c96 --- /dev/null +++ b/ssd_inception_v2_coco_2017_11_17/example_outputs/output_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8051cbe576541917d5a68bbdb6d119c2cf26bdf84aab362b909d8d1a8f9ad6c5 +size 458314 diff --git a/ssd_inception_v2_coco_2017_11_17/ssd_inception_v2_coco_2017_11_17_2026jul.onnx b/ssd_inception_v2_coco_2017_11_17/ssd_inception_v2_coco_2017_11_17_2026jul.onnx new file mode 100644 index 0000000000000000000000000000000000000000..f7084e0ab7ace44fd732f2205900d5f53fb26af5 --- /dev/null +++ b/ssd_inception_v2_coco_2017_11_17/ssd_inception_v2_coco_2017_11_17_2026jul.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5834f7214c7b9d80a9f708d23a3539d93e632fb346aeb5b1d013e63443354009 +size 102207336 diff --git a/ssd_mobilenet_v1_coco_2017_11_17/LICENSE b/ssd_mobilenet_v1_coco_2017_11_17/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..d3da228420e973edaf4123d5eeb42210f4450b0c --- /dev/null +++ b/ssd_mobilenet_v1_coco_2017_11_17/LICENSE @@ -0,0 +1,203 @@ +Copyright 2015 The TensorFlow Authors. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015, The TensorFlow Authors. + + 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. diff --git a/ssd_mobilenet_v1_coco_2017_11_17/README.md b/ssd_mobilenet_v1_coco_2017_11_17/README.md new file mode 100644 index 0000000000000000000000000000000000000000..572c44a69aa0cf547a7dbc1c579613b05d92b649 --- /dev/null +++ b/ssd_mobilenet_v1_coco_2017_11_17/README.md @@ -0,0 +1,48 @@ +# SSD MobileNet v1 COCO + +Object detection with a Single Shot MultiBox Detector (SSD) built on a MobileNet v1 backbone, +trained on the COCO dataset. The model was originally distributed as a frozen TensorFlow graph +(`ssd_mobilenet_v1_coco_2017_11_17.pb`) and converted to ONNX for use with OpenCV's DNN module. + +## Model Details +- **Architecture**: SSD (Single Shot MultiBox Detector) with MobileNet v1 backbone +- **Input**: RGB image, 300×300, raw uint8, NHWC layout (`image_tensor:0`, shape `[1, 300, 300, 3]`) +- **Output**: `detection_boxes:0` (normalized `ymin, xmin, ymax, xmax`), `detection_scores:0`, `detection_classes:0` (COCO class ids), `num_detections:0` +- **Framework**: ONNX (converted from the TensorFlow frozen graph via tf2onnx, opset 18) +- **Original weights**: http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_coco_2017_11_17.tar.gz + +## Usage + +### Python +```bash +python demo.py --model ssd_mobilenet_v1_coco_2017_11_17_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png --conf 0.3 +``` + +### C++ +The C++ demo runs inference with OpenCV's DNN module. Adjust the OpenCV paths to your setup: +```bash +OCV=/path/to/opencv # OpenCV source tree +OCVBUILD=/path/to/opencv/build # OpenCV build directory (generated headers + libs) +g++ -std=c++17 demo.cpp -o demo \ + -I$OCV/include \ + -I$OCV/modules/core/include \ + -I$OCV/modules/dnn/include \ + -I$OCV/modules/imgproc/include \ + -I$OCV/modules/imgcodecs/include \ + -I$OCVBUILD \ + -L$OCVBUILD/lib -Wl,-rpath,$OCVBUILD/lib -lopencv_dnn -lopencv_imgcodecs -lopencv_imgproc -lopencv_core +./demo --model ssd_mobilenet_v1_coco_2017_11_17_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png +``` + +## Conversion +The ONNX model was exported from the frozen TensorFlow graph with tf2onnx (opset 18) +via [convert_to_onnx.py](./convert_to_onnx.py) — input `image_tensor:0`, outputs +`detection_boxes:0`, `detection_scores:0`, `detection_classes:0`, `num_detections:0`. +Requires `tensorflow`, `tf2onnx`, and `onnx`. + +```bash +python convert_to_onnx.py --pb ../pb/ssd_mobilenet_v1_coco_2017_11_17.pb +``` + +## License +See [LICENSE](./LICENSE) — the model is released by the TensorFlow Authors under the Apache License 2.0. diff --git a/ssd_mobilenet_v1_coco_2017_11_17/convert_to_onnx.py b/ssd_mobilenet_v1_coco_2017_11_17/convert_to_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..6765d74a97174f65c9701b5855499020e5761513 --- /dev/null +++ b/ssd_mobilenet_v1_coco_2017_11_17/convert_to_onnx.py @@ -0,0 +1,40 @@ +import argparse +import datetime + +import onnx +import tensorflow as tf +import tf2onnx + + +def load_graph_def(pb_path): + with tf.io.gfile.GFile(pb_path, "rb") as f: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(f.read()) + return graph_def + + +def main(): + parser = argparse.ArgumentParser(description="Export ssd_mobilenet_v1_coco_2017_11_17.pb to ONNX") + parser.add_argument("--pb", default="../pb/ssd_mobilenet_v1_coco_2017_11_17.pb") + parser.add_argument("--opset", type=int, default=18) + args = parser.parse_args() + + graph_def = load_graph_def(args.pb) + + model_proto, _ = tf2onnx.convert.from_graph_def( + graph_def, + input_names=["image_tensor:0"], + output_names=["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"], + opset=args.opset, + ) + onnx.checker.check_model(model_proto) + + stamp = datetime.datetime.now().strftime("%Y%b").lower() + onnx_path = "ssd_mobilenet_v1_coco_2017_11_17_%s.onnx" % stamp + with open(onnx_path, "wb") as f: + f.write(model_proto.SerializeToString()) + print("wrote", onnx_path) + + +if __name__ == "__main__": + main() diff --git a/ssd_mobilenet_v1_coco_2017_11_17/demo.cpp b/ssd_mobilenet_v1_coco_2017_11_17/demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5fc21ac9ee788bf774b3a81f0321b1828954722e --- /dev/null +++ b/ssd_mobilenet_v1_coco_2017_11_17/demo.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace cv; + +static std::string argVal(int argc, char** argv, const std::string& key, const std::string& def) +{ + for (int i = 1; i + 1 < argc; ++i) + if (key == argv[i]) return argv[i + 1]; + return def; +} + +int main(int argc, char** argv) +{ + std::string model = argVal(argc, argv, "--model", "ssd_mobilenet_v1_coco_2017_11_17_2026jul.onnx"); + std::string image = argVal(argc, argv, "--image", "example_outputs/input_image.png"); + std::string output = argVal(argc, argv, "--output", "example_outputs/output_image.png"); + float conf = std::stof(argVal(argc, argv, "--conf", "0.3")); + + Mat img = imread(image); + if (img.empty()) + { + std::cerr << "could not read image: " << image << std::endl; + return 1; + } + + Mat rgb; + cvtColor(img, rgb, COLOR_BGR2RGB); + resize(rgb, rgb, Size(300, 300)); + if (!rgb.isContinuous()) rgb = rgb.clone(); + + int blobShape[] = {1, 300, 300, 3}; + Mat blob(4, blobShape, CV_8U, rgb.data); + dnn::Net net = dnn::readNetFromONNX(model, dnn::ENGINE_ORT); + net.setInput(blob); + std::vector out_str = {"detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"}; + std::vector outs; + net.forward(outs, out_str); + + const float *boxes = 0, *scores = 0, *classes = 0, *num = 0; + for (size_t i = 0; i < out_str.size(); ++i) + { + const std::string& n = out_str[i]; + if (n.find("detection_boxes") != std::string::npos) boxes = (const float*)outs[i].data; + else if (n.find("detection_scores") != std::string::npos) scores = (const float*)outs[i].data; + else if (n.find("detection_classes") != std::string::npos) classes = (const float*)outs[i].data; + else if (n.find("num_detections") != std::string::npos) num = (const float*)outs[i].data; + } + if (!boxes || !scores || !classes || !num) + { + std::cerr << "missing expected output tensors" << std::endl; + return 1; + } + + int nd = (int)num[0]; + int h = img.rows, w = img.cols; + Mat out = img.clone(); + std::vector lines; + for (int k = 0; k < nd; ++k) + { + if (scores[k] < conf) continue; + float ymin = boxes[k * 4 + 0], xmin = boxes[k * 4 + 1]; + float ymax = boxes[k * 4 + 2], xmax = boxes[k * 4 + 3]; + int cls = (int)classes[k]; + rectangle(out, Point(int(xmin * w), int(ymin * h)), Point(int(xmax * w), int(ymax * h)), Scalar(0, 255, 0), 2); + putText(out, format("%d:%.2f", cls, scores[k]), Point(int(xmin * w), int(ymin * h) - 5), + FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 1); + lines.push_back(format("%d %.3f %.3f %.3f %.3f %.3f", cls, scores[k], xmin, ymin, xmax, ymax)); + } + + imwrite(output, out); + std::cout << "ssd_mobilenet_v1_coco_2017_11_17 " << lines.size() << " detections" << std::endl; + for (const auto& l : lines) std::cout << l << std::endl; + return 0; +} diff --git a/ssd_mobilenet_v1_coco_2017_11_17/demo.py b/ssd_mobilenet_v1_coco_2017_11_17/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..76404b2b85ffc10db7b74837b1284e69dddabead --- /dev/null +++ b/ssd_mobilenet_v1_coco_2017_11_17/demo.py @@ -0,0 +1,52 @@ +import argparse +import glob +import os + +import cv2 as cv +import numpy as np + +here = os.path.dirname(os.path.abspath(__file__)) + + +def main(): + parser = argparse.ArgumentParser(description="SSD MobileNet v1 COCO (ONNX) object detection demo") + parser.add_argument("--model", default=(glob.glob(os.path.join(here, "*.onnx")) or [""])[0]) + parser.add_argument("--image", default=os.path.join(here, "example_outputs", "input_image.png")) + parser.add_argument("--output", default=os.path.join(here, "example_outputs", "output_image.png")) + parser.add_argument("--conf", type=float, default=0.3) + args = parser.parse_args() + + img = cv.imread(args.image) + if img is None: + raise SystemExit("could not read image: %s" % args.image) + + rgb = cv.resize(cv.cvtColor(img, cv.COLOR_BGR2RGB), (300, 300)) + + net = cv.dnn.readNetFromONNX(args.model, cv.dnn.ENGINE_ORT) + onames = ["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"] + net.setInput(rgb[None].astype(np.uint8)) + res = net.forward(onames) + boxes = res[[i for i, n in enumerate(onames) if "detection_boxes" in n][0]].reshape(-1, 4) + scores = res[[i for i, n in enumerate(onames) if "detection_scores" in n][0]].reshape(-1) + classes = res[[i for i, n in enumerate(onames) if "detection_classes" in n][0]].reshape(-1) + nd = int(res[[i for i, n in enumerate(onames) if "num_detections" in n][0]].reshape(-1)[0]) + + h, w = img.shape[:2] + out = img.copy() + kept = [] + for k in range(nd): + if scores[k] < args.conf: + continue + ymin, xmin, ymax, xmax = boxes[k] + kept.append((int(classes[k]), float(scores[k]), float(xmin), float(ymin), float(xmax), float(ymax))) + cv.rectangle(out, (int(xmin * w), int(ymin * h)), (int(xmax * w), int(ymax * h)), (0, 255, 0), 2) + cv.putText(out, "%d:%.2f" % (int(classes[k]), scores[k]), (int(xmin * w), int(ymin * h) - 5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) + + cv.imwrite(args.output, out) + print("ssd_mobilenet_v1_coco_2017_11_17", len(kept), "detections") + for c, s, xmin, ymin, xmax, ymax in kept: + print(c, round(s, 3), round(xmin, 3), round(ymin, 3), round(xmax, 3), round(ymax, 3)) + + +if __name__ == "__main__": + main() diff --git a/ssd_mobilenet_v1_coco_2017_11_17/example_outputs/input_image.png b/ssd_mobilenet_v1_coco_2017_11_17/example_outputs/input_image.png new file mode 100644 index 0000000000000000000000000000000000000000..326ece9829794ab70eaf0b1e3feac0a762099689 --- /dev/null +++ b/ssd_mobilenet_v1_coco_2017_11_17/example_outputs/input_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61db162464c0770138c5136af0e999ce3b6556244cec257945427396480d9caf +size 414310 diff --git a/ssd_mobilenet_v1_coco_2017_11_17/example_outputs/output_image.png b/ssd_mobilenet_v1_coco_2017_11_17/example_outputs/output_image.png new file mode 100644 index 0000000000000000000000000000000000000000..f98c3ce4d3b5da2169e3ef420404ec36df82be7a --- /dev/null +++ b/ssd_mobilenet_v1_coco_2017_11_17/example_outputs/output_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:953b498fb864bd5a1bf7d9abc47dfb86f20aac0954d45b8b77ae5acf379fe23a +size 328560 diff --git a/ssd_mobilenet_v1_coco_2017_11_17/ssd_mobilenet_v1_coco_2017_11_17_2026jul.onnx b/ssd_mobilenet_v1_coco_2017_11_17/ssd_mobilenet_v1_coco_2017_11_17_2026jul.onnx new file mode 100644 index 0000000000000000000000000000000000000000..35eefb06908cf91c302a78f5f14a13c502766929 --- /dev/null +++ b/ssd_mobilenet_v1_coco_2017_11_17/ssd_mobilenet_v1_coco_2017_11_17_2026jul.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7423295086c7ceaca3ccc0059c16989456a08c3e4b8c05e2cc415240f9148e65 +size 29289721 diff --git a/ssd_mobilenet_v1_ppn_coco/LICENSE b/ssd_mobilenet_v1_ppn_coco/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..4421e8e7b19941bd420a2723bb31d0abc15cd3d7 --- /dev/null +++ b/ssd_mobilenet_v1_ppn_coco/LICENSE @@ -0,0 +1,203 @@ +Copyright 2015 The TensorFlow Authors. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015, The TensorFlow Authors. + + 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. \ No newline at end of file diff --git a/ssd_mobilenet_v1_ppn_coco/README.md b/ssd_mobilenet_v1_ppn_coco/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a424ddd84ac5f2bc59784abbe81d5f18fe3c48ce --- /dev/null +++ b/ssd_mobilenet_v1_ppn_coco/README.md @@ -0,0 +1,49 @@ +# SSD MobileNet v1 PPN COCO + +Object detection with a Single Shot MultiBox Detector (SSD) built on a MobileNet v1 backbone with +a shared box predictor (Pooling Pyramid Network, PPN), trained on the COCO dataset. The model was +originally distributed as a frozen TensorFlow graph (`ssd_mobilenet_v1_ppn_coco.pb`) and converted +to ONNX for use with OpenCV's DNN module. + +## Model Details +- **Architecture**: SSD (Single Shot MultiBox Detector) with MobileNet v1 backbone and shared box predictor (Pooling Pyramid Network, PPN) +- **Input**: RGB image, 300×300, raw uint8, NHWC layout (`image_tensor:0`, shape `[1, 300, 300, 3]`) +- **Output**: `detection_boxes:0` (normalized `ymin, xmin, ymax, xmax`), `detection_scores:0`, `detection_classes:0` (COCO class ids), `num_detections:0` +- **Framework**: ONNX (converted from the TensorFlow frozen graph via tf2onnx, opset 18) +- **Original weights**: http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_ppn_shared_box_predictor_300x300_coco14_sync_2018_07_03.tar.gz + +## Usage + +### Python +```bash +python demo.py --model ssd_mobilenet_v1_ppn_coco_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png --conf 0.3 +``` + +### C++ +The C++ demo runs inference with OpenCV's DNN module. Adjust the OpenCV paths to your setup: +```bash +OCV=/path/to/opencv # OpenCV source tree +OCVBUILD=/path/to/opencv/build # OpenCV build directory (generated headers + libs) +g++ -std=c++17 demo.cpp -o demo \ + -I$OCV/include \ + -I$OCV/modules/core/include \ + -I$OCV/modules/dnn/include \ + -I$OCV/modules/imgproc/include \ + -I$OCV/modules/imgcodecs/include \ + -I$OCVBUILD \ + -L$OCVBUILD/lib -Wl,-rpath,$OCVBUILD/lib -lopencv_dnn -lopencv_imgcodecs -lopencv_imgproc -lopencv_core +./demo --model ssd_mobilenet_v1_ppn_coco_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png +``` + +## Conversion +The ONNX model was exported from the frozen TensorFlow graph with tf2onnx (opset 18) +via [convert_to_onnx.py](./convert_to_onnx.py) — input `image_tensor:0`, outputs +`detection_boxes:0`, `detection_scores:0`, `detection_classes:0`, `num_detections:0`. +Requires `tensorflow`, `tf2onnx`, and `onnx`. + +```bash +python convert_to_onnx.py --pb ../pb/ssd_mobilenet_v1_ppn_coco.pb +``` + +## License +See [LICENSE](./LICENSE) — the model is released by the TensorFlow Authors under the Apache License 2.0. diff --git a/ssd_mobilenet_v1_ppn_coco/convert_to_onnx.py b/ssd_mobilenet_v1_ppn_coco/convert_to_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..db8fd7d76150e97606f9e0427f268dbb2657d93a --- /dev/null +++ b/ssd_mobilenet_v1_ppn_coco/convert_to_onnx.py @@ -0,0 +1,40 @@ +import argparse +import datetime + +import onnx +import tensorflow as tf +import tf2onnx + + +def load_graph_def(pb_path): + with tf.io.gfile.GFile(pb_path, "rb") as f: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(f.read()) + return graph_def + + +def main(): + parser = argparse.ArgumentParser(description="Export ssd_mobilenet_v1_ppn_coco.pb to ONNX") + parser.add_argument("--pb", default="../pb/ssd_mobilenet_v1_ppn_coco.pb") + parser.add_argument("--opset", type=int, default=18) + args = parser.parse_args() + + graph_def = load_graph_def(args.pb) + + model_proto, _ = tf2onnx.convert.from_graph_def( + graph_def, + input_names=["image_tensor:0"], + output_names=["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"], + opset=args.opset, + ) + onnx.checker.check_model(model_proto) + + stamp = datetime.datetime.now().strftime("%Y%b").lower() + onnx_path = "ssd_mobilenet_v1_ppn_coco_%s.onnx" % stamp + with open(onnx_path, "wb") as f: + f.write(model_proto.SerializeToString()) + print("wrote", onnx_path) + + +if __name__ == "__main__": + main() diff --git a/ssd_mobilenet_v1_ppn_coco/demo.cpp b/ssd_mobilenet_v1_ppn_coco/demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd19390c22bdaf741aeaa6fb3726d6f80cc4ee43 --- /dev/null +++ b/ssd_mobilenet_v1_ppn_coco/demo.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace cv; + +static std::string argVal(int argc, char** argv, const std::string& key, const std::string& def) +{ + for (int i = 1; i + 1 < argc; ++i) + if (key == argv[i]) return argv[i + 1]; + return def; +} + +int main(int argc, char** argv) +{ + std::string model = argVal(argc, argv, "--model", "ssd_mobilenet_v1_ppn_coco_2026jul.onnx"); + std::string image = argVal(argc, argv, "--image", "example_outputs/input_image.png"); + std::string output = argVal(argc, argv, "--output", "example_outputs/output_image.png"); + float conf = std::stof(argVal(argc, argv, "--conf", "0.3")); + + Mat img = imread(image); + if (img.empty()) + { + std::cerr << "could not read image: " << image << std::endl; + return 1; + } + + Mat rgb; + cvtColor(img, rgb, COLOR_BGR2RGB); + resize(rgb, rgb, Size(300, 300)); + if (!rgb.isContinuous()) rgb = rgb.clone(); + + int blobShape[] = {1, 300, 300, 3}; + Mat blob(4, blobShape, CV_8U, rgb.data); + dnn::Net net = dnn::readNetFromONNX(model, dnn::ENGINE_ORT); + net.setInput(blob); + std::vector out_str = {"detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"}; + std::vector outs; + net.forward(outs, out_str); + + const float *boxes = 0, *scores = 0, *classes = 0, *num = 0; + for (size_t i = 0; i < out_str.size(); ++i) + { + const std::string& n = out_str[i]; + if (n.find("detection_boxes") != std::string::npos) boxes = (const float*)outs[i].data; + else if (n.find("detection_scores") != std::string::npos) scores = (const float*)outs[i].data; + else if (n.find("detection_classes") != std::string::npos) classes = (const float*)outs[i].data; + else if (n.find("num_detections") != std::string::npos) num = (const float*)outs[i].data; + } + if (!boxes || !scores || !classes || !num) + { + std::cerr << "missing expected output tensors" << std::endl; + return 1; + } + + int nd = (int)num[0]; + int h = img.rows, w = img.cols; + Mat out = img.clone(); + std::vector lines; + for (int k = 0; k < nd; ++k) + { + if (scores[k] < conf) continue; + float ymin = boxes[k * 4 + 0], xmin = boxes[k * 4 + 1]; + float ymax = boxes[k * 4 + 2], xmax = boxes[k * 4 + 3]; + int cls = (int)classes[k]; + rectangle(out, Point(int(xmin * w), int(ymin * h)), Point(int(xmax * w), int(ymax * h)), Scalar(0, 255, 0), 2); + putText(out, format("%d:%.2f", cls, scores[k]), Point(int(xmin * w), int(ymin * h) - 5), + FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 1); + lines.push_back(format("%d %.3f %.3f %.3f %.3f %.3f", cls, scores[k], xmin, ymin, xmax, ymax)); + } + + imwrite(output, out); + std::cout << "ssd_mobilenet_v1_ppn_coco " << lines.size() << " detections" << std::endl; + for (const auto& l : lines) std::cout << l << std::endl; + return 0; +} diff --git a/ssd_mobilenet_v1_ppn_coco/demo.py b/ssd_mobilenet_v1_ppn_coco/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..9ba70bac6336c34dc9da0416ef8d7c01b7bcacfd --- /dev/null +++ b/ssd_mobilenet_v1_ppn_coco/demo.py @@ -0,0 +1,52 @@ +import argparse +import glob +import os + +import cv2 as cv +import numpy as np + +here = os.path.dirname(os.path.abspath(__file__)) + + +def main(): + parser = argparse.ArgumentParser(description="SSD MobileNet v1 PPN COCO (ONNX) object detection demo") + parser.add_argument("--model", default=(glob.glob(os.path.join(here, "*.onnx")) or [""])[0]) + parser.add_argument("--image", default=os.path.join(here, "example_outputs", "input_image.png")) + parser.add_argument("--output", default=os.path.join(here, "example_outputs", "output_image.png")) + parser.add_argument("--conf", type=float, default=0.3) + args = parser.parse_args() + + img = cv.imread(args.image) + if img is None: + raise SystemExit("could not read image: %s" % args.image) + + rgb = cv.resize(cv.cvtColor(img, cv.COLOR_BGR2RGB), (300, 300)) + + net = cv.dnn.readNetFromONNX(args.model, cv.dnn.ENGINE_ORT) + onames = ["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"] + net.setInput(rgb[None].astype(np.uint8)) + res = net.forward(onames) + boxes = res[[i for i, n in enumerate(onames) if "detection_boxes" in n][0]].reshape(-1, 4) + scores = res[[i for i, n in enumerate(onames) if "detection_scores" in n][0]].reshape(-1) + classes = res[[i for i, n in enumerate(onames) if "detection_classes" in n][0]].reshape(-1) + nd = int(res[[i for i, n in enumerate(onames) if "num_detections" in n][0]].reshape(-1)[0]) + + h, w = img.shape[:2] + out = img.copy() + kept = [] + for k in range(nd): + if scores[k] < args.conf: + continue + ymin, xmin, ymax, xmax = boxes[k] + kept.append((int(classes[k]), float(scores[k]), float(xmin), float(ymin), float(xmax), float(ymax))) + cv.rectangle(out, (int(xmin * w), int(ymin * h)), (int(xmax * w), int(ymax * h)), (0, 255, 0), 2) + cv.putText(out, "%d:%.2f" % (int(classes[k]), scores[k]), (int(xmin * w), int(ymin * h) - 5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) + + cv.imwrite(args.output, out) + print("ssd_mobilenet_v1_ppn_coco", len(kept), "detections") + for c, s, xmin, ymin, xmax, ymax in kept: + print(c, round(s, 3), round(xmin, 3), round(ymin, 3), round(xmax, 3), round(ymax, 3)) + + +if __name__ == "__main__": + main() diff --git a/ssd_mobilenet_v1_ppn_coco/example_outputs/input_image.png b/ssd_mobilenet_v1_ppn_coco/example_outputs/input_image.png new file mode 100644 index 0000000000000000000000000000000000000000..326ece9829794ab70eaf0b1e3feac0a762099689 --- /dev/null +++ b/ssd_mobilenet_v1_ppn_coco/example_outputs/input_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61db162464c0770138c5136af0e999ce3b6556244cec257945427396480d9caf +size 414310 diff --git a/ssd_mobilenet_v1_ppn_coco/example_outputs/output_image.png b/ssd_mobilenet_v1_ppn_coco/example_outputs/output_image.png new file mode 100644 index 0000000000000000000000000000000000000000..ba2c544e3fe221f6edea733740d5345834472211 --- /dev/null +++ b/ssd_mobilenet_v1_ppn_coco/example_outputs/output_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b8c5b52639d95705ca0203fe15fc8a9257d5c88f632737a93edb55ce63ef3ad +size 324473 diff --git a/ssd_mobilenet_v1_ppn_coco/ssd_mobilenet_v1_ppn_coco_2026jul.onnx b/ssd_mobilenet_v1_ppn_coco/ssd_mobilenet_v1_ppn_coco_2026jul.onnx new file mode 100644 index 0000000000000000000000000000000000000000..901aa6759ef40db253b5c0d818c90e41d51c64d2 --- /dev/null +++ b/ssd_mobilenet_v1_ppn_coco/ssd_mobilenet_v1_ppn_coco_2026jul.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1b6be9dc0aca33aa1f78d34f2b32fe23097325f1482a837cfc4fc8aa62425ca +size 16189990 diff --git a/ssd_mobilenet_v2_coco_2018_03_29/LICENSE b/ssd_mobilenet_v2_coco_2018_03_29/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..12736ccd678d7be692590dcccf004a26233141df --- /dev/null +++ b/ssd_mobilenet_v2_coco_2018_03_29/LICENSE @@ -0,0 +1,212 @@ +Copyright 2022 Google LLC. All rights reserved. + +All files in the following folders: +/community +/official +/orbit +/research +/tensorflow_models + +Are licensed as follows: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016, The Authors. + + 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. diff --git a/ssd_mobilenet_v2_coco_2018_03_29/README.md b/ssd_mobilenet_v2_coco_2018_03_29/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5ca8176a2647c8a5737af83ee007770689eef627 --- /dev/null +++ b/ssd_mobilenet_v2_coco_2018_03_29/README.md @@ -0,0 +1,48 @@ +# SSD MobileNet v2 COCO + +Object detection with a Single Shot MultiBox Detector (SSD) built on a MobileNet v2 backbone, +trained on the COCO dataset. The model was originally distributed as a frozen TensorFlow graph +(`ssd_mobilenet_v2_coco_2018_03_29.pb`) and converted to ONNX for use with OpenCV's DNN module. + +## Model Details +- **Architecture**: SSD (Single Shot MultiBox Detector) with MobileNet v2 backbone +- **Input**: RGB image, 300×300, raw uint8, NHWC layout (`image_tensor:0`, shape `[1, 300, 300, 3]`) +- **Output**: `detection_boxes:0` (normalized `ymin, xmin, ymax, xmax`), `detection_scores:0`, `detection_classes:0` (COCO class ids), `num_detections:0` +- **Framework**: ONNX (converted from the TensorFlow frozen graph via tf2onnx, opset 18) +- **Original weights**: http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz + +## Usage + +### Python +```bash +python demo.py --model ssd_mobilenet_v2_coco_2018_03_29_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png --conf 0.3 +``` + +### C++ +The C++ demo runs inference with OpenCV's DNN module. Adjust the OpenCV paths to your setup: +```bash +OCV=/path/to/opencv # OpenCV source tree +OCVBUILD=/path/to/opencv/build # OpenCV build directory (generated headers + libs) +g++ -std=c++17 demo.cpp -o demo \ + -I$OCV/include \ + -I$OCV/modules/core/include \ + -I$OCV/modules/dnn/include \ + -I$OCV/modules/imgproc/include \ + -I$OCV/modules/imgcodecs/include \ + -I$OCVBUILD \ + -L$OCVBUILD/lib -Wl,-rpath,$OCVBUILD/lib -lopencv_dnn -lopencv_imgcodecs -lopencv_imgproc -lopencv_core +./demo --model ssd_mobilenet_v2_coco_2018_03_29_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png +``` + +## Conversion +The ONNX model was exported from the frozen TensorFlow graph with tf2onnx (opset 18) +via [convert_to_onnx.py](./convert_to_onnx.py) — input `image_tensor:0`, outputs +`detection_boxes:0`, `detection_scores:0`, `detection_classes:0`, `num_detections:0`. +Requires `tensorflow`, `tf2onnx`, and `onnx`. + +```bash +python convert_to_onnx.py --pb ../pb/ssd_mobilenet_v2_coco_2018_03_29.pb +``` + +## License +See [LICENSE](./LICENSE) — the model is released by the TensorFlow Authors under the Apache License 2.0. diff --git a/ssd_mobilenet_v2_coco_2018_03_29/convert_to_onnx.py b/ssd_mobilenet_v2_coco_2018_03_29/convert_to_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..80b9b8bb5223fc54de124a02b7c4d852b566ba14 --- /dev/null +++ b/ssd_mobilenet_v2_coco_2018_03_29/convert_to_onnx.py @@ -0,0 +1,40 @@ +import argparse +import datetime + +import onnx +import tensorflow as tf +import tf2onnx + + +def load_graph_def(pb_path): + with tf.io.gfile.GFile(pb_path, "rb") as f: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(f.read()) + return graph_def + + +def main(): + parser = argparse.ArgumentParser(description="Export ssd_mobilenet_v2_coco_2018_03_29.pb to ONNX") + parser.add_argument("--pb", default="../pb/ssd_mobilenet_v2_coco_2018_03_29.pb") + parser.add_argument("--opset", type=int, default=18) + args = parser.parse_args() + + graph_def = load_graph_def(args.pb) + + model_proto, _ = tf2onnx.convert.from_graph_def( + graph_def, + input_names=["image_tensor:0"], + output_names=["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"], + opset=args.opset, + ) + onnx.checker.check_model(model_proto) + + stamp = datetime.datetime.now().strftime("%Y%b").lower() + onnx_path = "ssd_mobilenet_v2_coco_2018_03_29_%s.onnx" % stamp + with open(onnx_path, "wb") as f: + f.write(model_proto.SerializeToString()) + print("wrote", onnx_path) + + +if __name__ == "__main__": + main() diff --git a/ssd_mobilenet_v2_coco_2018_03_29/demo.cpp b/ssd_mobilenet_v2_coco_2018_03_29/demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3fcfa482c48b0fafbeab20e33b7fd43894d14530 --- /dev/null +++ b/ssd_mobilenet_v2_coco_2018_03_29/demo.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace cv; + +static std::string argVal(int argc, char** argv, const std::string& key, const std::string& def) +{ + for (int i = 1; i + 1 < argc; ++i) + if (key == argv[i]) return argv[i + 1]; + return def; +} + +int main(int argc, char** argv) +{ + std::string model = argVal(argc, argv, "--model", "ssd_mobilenet_v2_coco_2018_03_29_2026jul.onnx"); + std::string image = argVal(argc, argv, "--image", "example_outputs/input_image.png"); + std::string output = argVal(argc, argv, "--output", "example_outputs/output_image.png"); + float conf = std::stof(argVal(argc, argv, "--conf", "0.3")); + + Mat img = imread(image); + if (img.empty()) + { + std::cerr << "could not read image: " << image << std::endl; + return 1; + } + + Mat rgb; + cvtColor(img, rgb, COLOR_BGR2RGB); + resize(rgb, rgb, Size(300, 300)); + if (!rgb.isContinuous()) rgb = rgb.clone(); + + int blobShape[] = {1, 300, 300, 3}; + Mat blob(4, blobShape, CV_8U, rgb.data); + dnn::Net net = dnn::readNetFromONNX(model, dnn::ENGINE_ORT); + net.setInput(blob); + std::vector out_str = {"detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"}; + std::vector outs; + net.forward(outs, out_str); + + const float *boxes = 0, *scores = 0, *classes = 0, *num = 0; + for (size_t i = 0; i < out_str.size(); ++i) + { + const std::string& n = out_str[i]; + if (n.find("detection_boxes") != std::string::npos) boxes = (const float*)outs[i].data; + else if (n.find("detection_scores") != std::string::npos) scores = (const float*)outs[i].data; + else if (n.find("detection_classes") != std::string::npos) classes = (const float*)outs[i].data; + else if (n.find("num_detections") != std::string::npos) num = (const float*)outs[i].data; + } + if (!boxes || !scores || !classes || !num) + { + std::cerr << "missing expected output tensors" << std::endl; + return 1; + } + + int nd = (int)num[0]; + int h = img.rows, w = img.cols; + Mat out = img.clone(); + std::vector lines; + for (int k = 0; k < nd; ++k) + { + if (scores[k] < conf) continue; + float ymin = boxes[k * 4 + 0], xmin = boxes[k * 4 + 1]; + float ymax = boxes[k * 4 + 2], xmax = boxes[k * 4 + 3]; + int cls = (int)classes[k]; + rectangle(out, Point(int(xmin * w), int(ymin * h)), Point(int(xmax * w), int(ymax * h)), Scalar(0, 255, 0), 2); + putText(out, format("%d:%.2f", cls, scores[k]), Point(int(xmin * w), int(ymin * h) - 5), + FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 1); + lines.push_back(format("%d %.3f %.3f %.3f %.3f %.3f", cls, scores[k], xmin, ymin, xmax, ymax)); + } + + imwrite(output, out); + std::cout << "ssd_mobilenet_v2_coco_2018_03_29 " << lines.size() << " detections" << std::endl; + for (const auto& l : lines) std::cout << l << std::endl; + return 0; +} diff --git a/ssd_mobilenet_v2_coco_2018_03_29/demo.py b/ssd_mobilenet_v2_coco_2018_03_29/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..973e1e38c5b722bc30b52815d252bfd28a97dd9a --- /dev/null +++ b/ssd_mobilenet_v2_coco_2018_03_29/demo.py @@ -0,0 +1,52 @@ +import argparse +import glob +import os + +import cv2 as cv +import numpy as np + +here = os.path.dirname(os.path.abspath(__file__)) + + +def main(): + parser = argparse.ArgumentParser(description="SSD MobileNet v2 COCO (ONNX) object detection demo") + parser.add_argument("--model", default=(glob.glob(os.path.join(here, "*.onnx")) or [""])[0]) + parser.add_argument("--image", default=os.path.join(here, "example_outputs", "input_image.png")) + parser.add_argument("--output", default=os.path.join(here, "example_outputs", "output_image.png")) + parser.add_argument("--conf", type=float, default=0.3) + args = parser.parse_args() + + img = cv.imread(args.image) + if img is None: + raise SystemExit("could not read image: %s" % args.image) + + rgb = cv.resize(cv.cvtColor(img, cv.COLOR_BGR2RGB), (300, 300)) + + net = cv.dnn.readNetFromONNX(args.model, cv.dnn.ENGINE_ORT) + onames = ["detection_boxes:0", "detection_scores:0", "detection_classes:0", "num_detections:0"] + net.setInput(rgb[None].astype(np.uint8)) + res = net.forward(onames) + boxes = res[[i for i, n in enumerate(onames) if "detection_boxes" in n][0]].reshape(-1, 4) + scores = res[[i for i, n in enumerate(onames) if "detection_scores" in n][0]].reshape(-1) + classes = res[[i for i, n in enumerate(onames) if "detection_classes" in n][0]].reshape(-1) + nd = int(res[[i for i, n in enumerate(onames) if "num_detections" in n][0]].reshape(-1)[0]) + + h, w = img.shape[:2] + out = img.copy() + kept = [] + for k in range(nd): + if scores[k] < args.conf: + continue + ymin, xmin, ymax, xmax = boxes[k] + kept.append((int(classes[k]), float(scores[k]), float(xmin), float(ymin), float(xmax), float(ymax))) + cv.rectangle(out, (int(xmin * w), int(ymin * h)), (int(xmax * w), int(ymax * h)), (0, 255, 0), 2) + cv.putText(out, "%d:%.2f" % (int(classes[k]), scores[k]), (int(xmin * w), int(ymin * h) - 5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) + + cv.imwrite(args.output, out) + print("ssd_mobilenet_v2_coco_2018_03_29", len(kept), "detections") + for c, s, xmin, ymin, xmax, ymax in kept: + print(c, round(s, 3), round(xmin, 3), round(ymin, 3), round(xmax, 3), round(ymax, 3)) + + +if __name__ == "__main__": + main() diff --git a/ssd_mobilenet_v2_coco_2018_03_29/example_outputs/input_image.png b/ssd_mobilenet_v2_coco_2018_03_29/example_outputs/input_image.png new file mode 100644 index 0000000000000000000000000000000000000000..d892556078d2c17296761359f03ac8dca9b1be26 --- /dev/null +++ b/ssd_mobilenet_v2_coco_2018_03_29/example_outputs/input_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d711ef10627f93def79c0c6ec2d0fc3da06cbb7e426d81fd2782538c3c549f52 +size 508341 diff --git a/ssd_mobilenet_v2_coco_2018_03_29/example_outputs/output_image.png b/ssd_mobilenet_v2_coco_2018_03_29/example_outputs/output_image.png new file mode 100644 index 0000000000000000000000000000000000000000..47f0db9fe8b165d205bb4f534b419496d2e78bbd --- /dev/null +++ b/ssd_mobilenet_v2_coco_2018_03_29/example_outputs/output_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85d11aee65d0e2c475c379eed94a08fd0b510c310c72b95e251fdd8ff16fe18f +size 458003 diff --git a/ssd_mobilenet_v2_coco_2018_03_29/ssd_mobilenet_v2_coco_2018_03_29_2026jul.onnx b/ssd_mobilenet_v2_coco_2018_03_29/ssd_mobilenet_v2_coco_2018_03_29_2026jul.onnx new file mode 100644 index 0000000000000000000000000000000000000000..406163b37261dd00e8f6481ae326d4fdb18f9808 --- /dev/null +++ b/ssd_mobilenet_v2_coco_2018_03_29/ssd_mobilenet_v2_coco_2018_03_29_2026jul.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ba2fdaa87b8cbbb52c16b5c6e31a7452c00e8ad68aec580bfb7b07f5b212619 +size 69584537 diff --git a/tensorflow_inception_graph/LICENSE b/tensorflow_inception_graph/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..4421e8e7b19941bd420a2723bb31d0abc15cd3d7 --- /dev/null +++ b/tensorflow_inception_graph/LICENSE @@ -0,0 +1,203 @@ +Copyright 2015 The TensorFlow Authors. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015, The TensorFlow Authors. + + 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. \ No newline at end of file diff --git a/tensorflow_inception_graph/README.md b/tensorflow_inception_graph/README.md new file mode 100644 index 0000000000000000000000000000000000000000..bf6f5361975361333490600e176e0d151d4fd1cf --- /dev/null +++ b/tensorflow_inception_graph/README.md @@ -0,0 +1,55 @@ +# TensorFlow Inception + +Image classification with the Inception v1 (GoogLeNet) network trained on ImageNet. +The model was originally distributed as a frozen TensorFlow graph +(`tensorflow_inception_graph.pb`) and converted to ONNX for use with OpenCV's DNN module. + +## Model Details +- **Architecture**: Inception v1 / GoogLeNet +- **Input**: RGB image, 224×224, raw 0–255 float, NHWC layout (`input:0`, shape `[1, 224, 224, 3]`) +- **Output**: ImageNet class scores, softmax over 1008 classes (`softmax2:0`) +- **Framework**: ONNX (converted from the TensorFlow frozen graph via tf2onnx, opset 18) +- **Original weights**: https://github.com/petewarden/tf_ios_makefile_example/raw/master/data/tensorflow_inception_graph.pb + +## Usage + +### Python +```bash +python demo.py --model tensorflow_inception_graph_2026jul.onnx --image example_outputs/input_image.png --output example_outputs/output_image.png +``` + +Or import directly: +```python +import cv2 + +net = cv2.dnn.readNet("tensorflow_inception_graph_2026jul.onnx") +# see demo.py for the full inference pipeline +``` + +### C++ +The C++ demo runs inference with OpenCV's DNN module. Adjust the OpenCV paths to your setup: +```bash +OCV=/path/to/opencv # OpenCV source tree +OCVBUILD=/path/to/opencv/build # OpenCV build directory (generated headers + libs) +g++ -std=c++17 demo.cpp -o demo \ + -I$OCV/include \ + -I$OCV/modules/core/include \ + -I$OCV/modules/dnn/include \ + -I$OCV/modules/imgproc/include \ + -I$OCV/modules/imgcodecs/include \ + -I$OCVBUILD \ + -L$OCVBUILD/lib -Wl,-rpath,$OCVBUILD/lib -lopencv_dnn -lopencv_imgcodecs -lopencv_imgproc -lopencv_core +./demo --model tensorflow_inception_graph_2026jul.onnx --image example_outputs/input_image.png +``` + +## Conversion +The ONNX model was exported from the frozen TensorFlow graph with tf2onnx (opset 18) +via [convert_to_onnx.py](./convert_to_onnx.py) — inputs `input:0`, outputs `softmax2:0`, +input shape overridden to `[1, 224, 224, 3]`. Requires `tensorflow`, `tf2onnx`, and `onnx`. + +```bash +python convert_to_onnx.py --pb ../pb/tensorflow_inception_graph.pb +``` + +## License +See [LICENSE](./LICENSE) — the model is released by the TensorFlow Authors under the Apache License 2.0. diff --git a/tensorflow_inception_graph/convert_to_onnx.py b/tensorflow_inception_graph/convert_to_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..e9e2b56a972b3ff4bbbee3405e04e123684e3706 --- /dev/null +++ b/tensorflow_inception_graph/convert_to_onnx.py @@ -0,0 +1,41 @@ +import argparse +import datetime + +import onnx +import tensorflow as tf +import tf2onnx + + +def load_graph_def(pb_path): + with tf.io.gfile.GFile(pb_path, "rb") as f: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(f.read()) + return graph_def + + +def main(): + parser = argparse.ArgumentParser(description="Export tensorflow_inception_graph.pb to ONNX") + parser.add_argument("--pb", default="../pb/tensorflow_inception_graph.pb") + parser.add_argument("--opset", type=int, default=18) + args = parser.parse_args() + + graph_def = load_graph_def(args.pb) + + model_proto, _ = tf2onnx.convert.from_graph_def( + graph_def, + input_names=["input:0"], + output_names=["softmax2:0"], + opset=args.opset, + shape_override={"input:0": [1, 224, 224, 3]}, + ) + onnx.checker.check_model(model_proto) + + stamp = datetime.datetime.now().strftime("%Y%b").lower() + onnx_path = "tensorflow_inception_graph_%s.onnx" % stamp + with open(onnx_path, "wb") as f: + f.write(model_proto.SerializeToString()) + print("wrote", onnx_path) + + +if __name__ == "__main__": + main() diff --git a/tensorflow_inception_graph/demo.cpp b/tensorflow_inception_graph/demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4567a6060f9c446fd9849d13c8dc0355ca6856f --- /dev/null +++ b/tensorflow_inception_graph/demo.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static std::string argVal(int argc, char** argv, const std::string& key, const std::string& def) +{ + for (int i = 1; i + 1 < argc; ++i) + if (key == argv[i]) return argv[i + 1]; + return def; +} + +int main(int argc, char** argv) +{ + std::string model = argVal(argc, argv, "--model", "tensorflow_inception_graph_2026jul.onnx"); + std::string image = argVal(argc, argv, "--image", "example_outputs/input_image.png"); + std::string output = argVal(argc, argv, "--output", "example_outputs/output_image.png"); + std::string labels = argVal(argc, argv, "--labels", ""); + + cv::Mat img = cv::imread(image); + if (img.empty()) + { + std::cerr << "could not read image: " << image << std::endl; + return 1; + } + + cv::Mat rgb; + cv::cvtColor(img, rgb, cv::COLOR_BGR2RGB); + cv::resize(rgb, rgb, cv::Size(224, 224)); + rgb.convertTo(rgb, CV_32F); + if (!rgb.isContinuous()) rgb = rgb.clone(); + + int blobShape[] = {1, 224, 224, 3}; + cv::Mat blob(4, blobShape, CV_32F, rgb.data); + cv::dnn::Net net = cv::dnn::readNetFromONNX(model); + net.setInput(blob); + cv::Mat scoresMat = net.forward(); + float* scores = (float*)scoresMat.data; + int n = (int)scoresMat.total(); + int top = (int)(std::max_element(scores, scores + n) - scores); + float conf = scores[top]; + + std::string label = std::to_string(top); + if (!labels.empty()) + { + std::ifstream f(labels); + std::vector names; + std::string line; + while (std::getline(f, line)) names.push_back(line); + if (top < (int)names.size()) label = names[top]; + } + std::cout << "class " << top << " " << label << " confidence " << conf << std::endl; + + cv::Mat out = img.clone(); + cv::putText(out, cv::format("%s (%.2f)", label.c_str(), conf), cv::Point(10, 30), + cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 255, 0), 2); + cv::imwrite(output, out); + std::cout << "wrote " << output << std::endl; + return 0; +} diff --git a/tensorflow_inception_graph/demo.py b/tensorflow_inception_graph/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..66afbfcdf6a8965d8fafa791cecad644d0672aa0 --- /dev/null +++ b/tensorflow_inception_graph/demo.py @@ -0,0 +1,45 @@ +import argparse +import os + +import cv2 as cv +import numpy as np + +here = os.path.dirname(os.path.abspath(__file__)) + + +def main(): + parser = argparse.ArgumentParser(description="TensorFlow Inception (ONNX) image classification demo") + parser.add_argument("--model", default=os.path.join(here, "tensorflow_inception_graph_2026jul.onnx")) + parser.add_argument("--image", default=os.path.join(here, "example_outputs", "input_image.png")) + parser.add_argument("--output", default=os.path.join(here, "example_outputs", "output_image.png")) + parser.add_argument("--labels", help="optional ImageNet label file, one class name per line") + args = parser.parse_args() + + img = cv.imread(args.image) + if img is None: + raise SystemExit("could not read image: %s" % args.image) + + rgb = cv.resize(cv.cvtColor(img, cv.COLOR_BGR2RGB), (224, 224)).astype(np.float32) + + net = cv.dnn.readNetFromONNX(args.model) + net.setInput(rgb[None]) + scores = net.forward().ravel() + + top = int(np.argmax(scores)) + conf = float(scores[top]) + label = str(top) + if args.labels: + names = open(args.labels).read().splitlines() + if top < len(names): + label = names[top] + + print("class", top, label, "confidence", round(conf, 4)) + + out = img.copy() + cv.putText(out, "%s (%.2f)" % (label, conf), (10, 30), cv.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) + cv.imwrite(args.output, out) + print("wrote", args.output) + + +if __name__ == "__main__": + main() diff --git a/tensorflow_inception_graph/example_outputs/input_image.png b/tensorflow_inception_graph/example_outputs/input_image.png new file mode 100644 index 0000000000000000000000000000000000000000..801b36179c90aa4c5c2328b16e8dc64b765693ff --- /dev/null +++ b/tensorflow_inception_graph/example_outputs/input_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9f5173e165918db972a36b61f73d9548f43a9859ebbcc4b5603c887f5bb6e44 +size 94297 diff --git a/tensorflow_inception_graph/example_outputs/output_image.png b/tensorflow_inception_graph/example_outputs/output_image.png new file mode 100644 index 0000000000000000000000000000000000000000..b80e705f4c240de016dfb5c4c02fe8cd9148679c --- /dev/null +++ b/tensorflow_inception_graph/example_outputs/output_image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22a641062907e4da94ca04449ee6a36e0dc8ea5e70afed32b117e17bc5323a10 +size 110697 diff --git a/tensorflow_inception_graph/tensorflow_inception_graph_2026jul.onnx b/tensorflow_inception_graph/tensorflow_inception_graph_2026jul.onnx new file mode 100644 index 0000000000000000000000000000000000000000..5cd61a00514a3981bb4512a3c07c960e37edcf26 --- /dev/null +++ b/tensorflow_inception_graph/tensorflow_inception_graph_2026jul.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:048dabba50bb14a8038cda21322475f8d82781fd1f94b956003d89da5b50c961 +size 28289836