From 87339473e4538f3bcc2a9401a32347ece613c3c3 Mon Sep 17 00:00:00 2001 From: Wenwei Zhang <40779233+ZwwWayne@users.noreply.github.com> Date: Tue, 14 Jul 2020 23:53:11 +0800 Subject: [PATCH] Support pypi (#19) * add publish workflow * Add manifest * Clean files and bump to 0.5.0 * Fix MANIFEST --- .github/workflows/publish-to-pypi.yml | 20 ++ MANIFEST.in | 4 + mmdet3d/VERSION | 2 +- mmdet3d/ops/iou3d/src/iou3d.cpp | 9 + mmdet3d/ops/iou3d/src/iou3d_kernel.cu | 9 + mmdet3d/ops/spconv/include/spconv/box_iou.h | 156 -------------- mmdet3d/ops/spconv/include/spconv/nms.h | 196 ------------------ .../ops/spconv/include/spconv/nms_functor.h | 37 ---- mmdet3d/ops/spconv/include/spconv/nms_gpu.h | 18 -- mmdet3d/ops/spconv/include/spconv/nms_ops.h | 75 ------- setup.py | 18 +- 11 files changed, 51 insertions(+), 493 deletions(-) create mode 100644 .github/workflows/publish-to-pypi.yml create mode 100644 MANIFEST.in delete mode 100644 mmdet3d/ops/spconv/include/spconv/box_iou.h delete mode 100644 mmdet3d/ops/spconv/include/spconv/nms.h delete mode 100644 mmdet3d/ops/spconv/include/spconv/nms_functor.h delete mode 100644 mmdet3d/ops/spconv/include/spconv/nms_gpu.h delete mode 100644 mmdet3d/ops/spconv/include/spconv/nms_ops.h diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml new file mode 100644 index 0000000000..5404fff457 --- /dev/null +++ b/.github/workflows/publish-to-pypi.yml @@ -0,0 +1,20 @@ +name: deploy + +on: push + +jobs: + build-n-publish: + runs-on: ubuntu-latest + if: startsWith(github.event.ref, 'refs/tags') + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Build MMDet3D + run: python setup.py sdist + - name: Publish distribution to PyPI + run: | + pip install twine + twine upload dist/* -u __token__ -p ${{ secrets.pypi_password }} diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000000..817d05d39e --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include requirements/*.txt +include mmdet3d/ops/**/*.cpp mmdet3d/ops/**/*.cu +include mmdet3d/ops/**/*.h mmdet3d/ops/**/*.cc +include mmdet3d/VERSION diff --git a/mmdet3d/VERSION b/mmdet3d/VERSION index 8acdd82b76..8f0916f768 100644 --- a/mmdet3d/VERSION +++ b/mmdet3d/VERSION @@ -1 +1 @@ -0.0.1 +0.5.0 diff --git a/mmdet3d/ops/iou3d/src/iou3d.cpp b/mmdet3d/ops/iou3d/src/iou3d.cpp index a1726cd45a..f5158d5f23 100644 --- a/mmdet3d/ops/iou3d/src/iou3d.cpp +++ b/mmdet3d/ops/iou3d/src/iou3d.cpp @@ -1,3 +1,12 @@ +// Modified from +// https://github.com/open-mmlab/OpenPCDet/blob/master/pcdet/ops/iou3d_nms/src/iou3d_nms.cpp + +/* +3D IoU Calculation and Rotated NMS(modified from 2D NMS written by others) +Written by Shaoshuai Shi +All Rights Reserved 2019-2020. +*/ + #include #include #include diff --git a/mmdet3d/ops/iou3d/src/iou3d_kernel.cu b/mmdet3d/ops/iou3d/src/iou3d_kernel.cu index 9549025902..fce3f78825 100644 --- a/mmdet3d/ops/iou3d/src/iou3d_kernel.cu +++ b/mmdet3d/ops/iou3d/src/iou3d_kernel.cu @@ -1,3 +1,12 @@ +// Modified from +// https://github.com/open-mmlab/OpenPCDet/blob/master/pcdet/ops/iou3d_nms/src/iou3d_nms_kernel.cu + +/* +3D IoU Calculation and Rotated NMS(modified from 2D NMS written by others) +Written by Shaoshuai Shi +All Rights Reserved 2019-2020. +*/ + #include #define THREADS_PER_BLOCK 16 #define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0)) diff --git a/mmdet3d/ops/spconv/include/spconv/box_iou.h b/mmdet3d/ops/spconv/include/spconv/box_iou.h deleted file mode 100644 index 3a56b1841c..0000000000 --- a/mmdet3d/ops/spconv/include/spconv/box_iou.h +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2019 Yan Yan -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef BOX_IOU_H -#define BOX_IOU_H - -#include -// must include pybind11/eigen.h if using eigen matrix as arguments. -#include - -#include -#include - -namespace spconv { -// #include "voxelnet/core/cc/pybind11_helper.h" -namespace py = pybind11; -using namespace pybind11::literals; -template -inline py::array_t constant(ShapeContainer shape, DType value) { - // create ROWMAJOR array. - py::array_t array(shape); - std::fill(array.mutable_data(), array.mutable_data() + array.size(), value); - return array; -} - -template -inline py::array_t zeros(std::vector shape) { - return constant>(shape, 0); -} - -template -py::array_t rbbox_iou(py::array_t box_corners, - py::array_t qbox_corners, - py::array_t standup_iou, - DType standup_thresh) { - namespace bg = boost::geometry; - typedef bg::model::point point_t; - typedef bg::model::polygon polygon_t; - polygon_t poly, qpoly; - std::vector poly_inter, poly_union; - DType inter_area, union_area; - auto box_corners_r = box_corners.template unchecked<3>(); - auto qbox_corners_r = qbox_corners.template unchecked<3>(); - auto standup_iou_r = standup_iou.template unchecked<2>(); - auto N = box_corners_r.shape(0); - auto K = qbox_corners_r.shape(0); - py::array_t overlaps = zeros({int(N), int(K)}); - auto overlaps_rw = overlaps.template mutable_unchecked<2>(); - if (N == 0 || K == 0) { - return overlaps; - } - for (int k = 0; k < K; ++k) { - for (int n = 0; n < N; ++n) { - if (standup_iou_r(n, k) <= standup_thresh) continue; - bg::append(poly, point_t(box_corners_r(n, 0, 0), box_corners_r(n, 0, 1))); - bg::append(poly, point_t(box_corners_r(n, 1, 0), box_corners_r(n, 1, 1))); - bg::append(poly, point_t(box_corners_r(n, 2, 0), box_corners_r(n, 2, 1))); - bg::append(poly, point_t(box_corners_r(n, 3, 0), box_corners_r(n, 3, 1))); - bg::append(poly, point_t(box_corners_r(n, 0, 0), box_corners_r(n, 0, 1))); - bg::append(qpoly, - point_t(qbox_corners_r(k, 0, 0), qbox_corners_r(k, 0, 1))); - bg::append(qpoly, - point_t(qbox_corners_r(k, 1, 0), qbox_corners_r(k, 1, 1))); - bg::append(qpoly, - point_t(qbox_corners_r(k, 2, 0), qbox_corners_r(k, 2, 1))); - bg::append(qpoly, - point_t(qbox_corners_r(k, 3, 0), qbox_corners_r(k, 3, 1))); - bg::append(qpoly, - point_t(qbox_corners_r(k, 0, 0), qbox_corners_r(k, 0, 1))); - - bg::intersection(poly, qpoly, poly_inter); - - if (!poly_inter.empty()) { - inter_area = bg::area(poly_inter.front()); - bg::union_(poly, qpoly, poly_union); - if (!poly_union.empty()) { - union_area = bg::area(poly_union.front()); - overlaps_rw(n, k) = inter_area / union_area; - } - poly_union.clear(); - } - poly.clear(); - qpoly.clear(); - poly_inter.clear(); - } - } - return overlaps; -} - -template -py::array_t rbbox_intersection(py::array_t box_corners, - py::array_t qbox_corners, - py::array_t standup_iou, - DType standup_thresh) { - namespace bg = boost::geometry; - typedef bg::model::point point_t; - typedef bg::model::polygon polygon_t; - polygon_t poly, qpoly; - std::vector poly_inter, poly_union; - DType inter_area, union_area; - auto box_corners_r = box_corners.template unchecked<3>(); - auto qbox_corners_r = qbox_corners.template unchecked<3>(); - auto standup_iou_r = standup_iou.template unchecked<2>(); - auto N = box_corners_r.shape(0); - auto K = qbox_corners_r.shape(0); - py::array_t overlaps = zeros({int(N), int(K)}); - auto overlaps_rw = overlaps.template mutable_unchecked<2>(); - if (N == 0 || K == 0) { - return overlaps; - } - for (int k = 0; k < K; ++k) { - for (int n = 0; n < N; ++n) { - if (standup_iou_r(n, k) <= standup_thresh) continue; - bg::append(poly, point_t(box_corners_r(n, 0, 0), box_corners_r(n, 0, 1))); - bg::append(poly, point_t(box_corners_r(n, 1, 0), box_corners_r(n, 1, 1))); - bg::append(poly, point_t(box_corners_r(n, 2, 0), box_corners_r(n, 2, 1))); - bg::append(poly, point_t(box_corners_r(n, 3, 0), box_corners_r(n, 3, 1))); - bg::append(poly, point_t(box_corners_r(n, 0, 0), box_corners_r(n, 0, 1))); - bg::append(qpoly, - point_t(qbox_corners_r(k, 0, 0), qbox_corners_r(k, 0, 1))); - bg::append(qpoly, - point_t(qbox_corners_r(k, 1, 0), qbox_corners_r(k, 1, 1))); - bg::append(qpoly, - point_t(qbox_corners_r(k, 2, 0), qbox_corners_r(k, 2, 1))); - bg::append(qpoly, - point_t(qbox_corners_r(k, 3, 0), qbox_corners_r(k, 3, 1))); - bg::append(qpoly, - point_t(qbox_corners_r(k, 0, 0), qbox_corners_r(k, 0, 1))); - - bg::intersection(poly, qpoly, poly_inter); - - if (!poly_inter.empty()) { - inter_area = bg::area(poly_inter.front()); - overlaps_rw(n, k) = inter_area; - } - poly.clear(); - qpoly.clear(); - poly_inter.clear(); - } - } - return overlaps; -} - -} // namespace spconv -#endif diff --git a/mmdet3d/ops/spconv/include/spconv/nms.h b/mmdet3d/ops/spconv/include/spconv/nms.h deleted file mode 100644 index e24d427556..0000000000 --- a/mmdet3d/ops/spconv/include/spconv/nms.h +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2019 Yan Yan -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef NMS_CPU_H -#define NMS_CPU_H -#include -// must include pybind11/stl.h if using containers in STL in arguments. -#include -#include - -#include -#include -#include - -#include "box_iou.h" -#include "nms_gpu.h" -namespace spconv { -namespace py = pybind11; -using namespace pybind11::literals; - -template -std::vector non_max_suppression_cpu(py::array_t boxes, - py::array_t order, DType thresh, - DType eps = 0) { - auto ndets = boxes.shape(0); - auto boxes_r = boxes.template unchecked<2>(); - auto order_r = order.template unchecked<1>(); - auto suppressed = zeros({int(ndets)}); - auto suppressed_rw = suppressed.template mutable_unchecked<1>(); - auto area = zeros({int(ndets)}); - auto area_rw = area.template mutable_unchecked<1>(); - // get areas - for (int i = 0; i < ndets; ++i) { - area_rw(i) = (boxes_r(i, 2) - boxes_r(i, 0) + eps) * - (boxes_r(i, 3) - boxes_r(i, 1) + eps); - } - std::vector keep; - int i, j; - DType xx1, xx2, w, h, inter, ovr; - for (int _i = 0; _i < ndets; ++_i) { - i = order_r(_i); - if (suppressed_rw(i) == 1) continue; - keep.push_back(i); - for (int _j = _i + 1; _j < ndets; ++_j) { - j = order_r(_j); - if (suppressed_rw(j) == 1) continue; - xx2 = std::min(boxes_r(i, 2), boxes_r(j, 2)); - xx1 = std::max(boxes_r(i, 0), boxes_r(j, 0)); - w = xx2 - xx1 + eps; - if (w > 0) { - xx2 = std::min(boxes_r(i, 3), boxes_r(j, 3)); - xx1 = std::max(boxes_r(i, 1), boxes_r(j, 1)); - h = xx2 - xx1 + eps; - if (h > 0) { - inter = w * h; - ovr = inter / (area_rw(i) + area_rw(j) - inter); - if (ovr >= thresh) suppressed_rw(j) = 1; - } - } - } - } - return keep; -} - -template -std::vector rotate_non_max_suppression_cpu(py::array_t box_corners, - py::array_t order, - py::array_t standup_iou, - DType thresh) { - auto ndets = box_corners.shape(0); - auto box_corners_r = box_corners.template unchecked<3>(); - auto order_r = order.template unchecked<1>(); - auto suppressed = zeros({int(ndets)}); - auto suppressed_rw = suppressed.template mutable_unchecked<1>(); - auto standup_iou_r = standup_iou.template unchecked<2>(); - std::vector keep; - int i, j; - - namespace bg = boost::geometry; - typedef bg::model::point point_t; - typedef bg::model::polygon polygon_t; - polygon_t poly, qpoly; - std::vector poly_inter, poly_union; - DType inter_area, union_area, overlap; - - for (int _i = 0; _i < ndets; ++_i) { - i = order_r(_i); - if (suppressed_rw(i) == 1) continue; - keep.push_back(i); - for (int _j = _i + 1; _j < ndets; ++_j) { - j = order_r(_j); - if (suppressed_rw(j) == 1) continue; - if (standup_iou_r(i, j) <= 0.0) continue; - // std::cout << "pre_poly" << std::endl; - try { - bg::append(poly, - point_t(box_corners_r(i, 0, 0), box_corners_r(i, 0, 1))); - bg::append(poly, - point_t(box_corners_r(i, 1, 0), box_corners_r(i, 1, 1))); - bg::append(poly, - point_t(box_corners_r(i, 2, 0), box_corners_r(i, 2, 1))); - bg::append(poly, - point_t(box_corners_r(i, 3, 0), box_corners_r(i, 3, 1))); - bg::append(poly, - point_t(box_corners_r(i, 0, 0), box_corners_r(i, 0, 1))); - bg::append(qpoly, - point_t(box_corners_r(j, 0, 0), box_corners_r(j, 0, 1))); - bg::append(qpoly, - point_t(box_corners_r(j, 1, 0), box_corners_r(j, 1, 1))); - bg::append(qpoly, - point_t(box_corners_r(j, 2, 0), box_corners_r(j, 2, 1))); - bg::append(qpoly, - point_t(box_corners_r(j, 3, 0), box_corners_r(j, 3, 1))); - bg::append(qpoly, - point_t(box_corners_r(j, 0, 0), box_corners_r(j, 0, 1))); - bg::intersection(poly, qpoly, poly_inter); - } catch (const std::exception &e) { - std::cout << "box i corners:" << std::endl; - for (int k = 0; k < 4; ++k) { - std::cout << box_corners_r(i, k, 0) << " " << box_corners_r(i, k, 1) - << std::endl; - } - std::cout << "box j corners:" << std::endl; - for (int k = 0; k < 4; ++k) { - std::cout << box_corners_r(j, k, 0) << " " << box_corners_r(j, k, 1) - << std::endl; - } - // throw e; - continue; - } - // std::cout << "post_poly" << std::endl; - // std::cout << "post_intsec" << std::endl; - if (!poly_inter.empty()) { - inter_area = bg::area(poly_inter.front()); - // std::cout << "pre_union" << " " << inter_area << std::endl; - bg::union_(poly, qpoly, poly_union); - /* - if (poly_union.empty()){ - std::cout << "intsec area:" << " " << inter_area << std::endl; - std::cout << "box i corners:" << std::endl; - for(int k = 0; k < 4; ++k){ - std::cout << box_corners_r(i, k, 0) << " " << box_corners_r(i, - k, 1) << std::endl; - } - std::cout << "box j corners:" << std::endl; - for(int k = 0; k < 4; ++k){ - std::cout << box_corners_r(j, k, 0) << " " << box_corners_r(j, - k, 1) << std::endl; - } - }*/ - // std::cout << "post_union" << poly_union.empty() << std::endl; - if (!poly_union.empty()) { // ignore invalid box - union_area = bg::area(poly_union.front()); - // std::cout << "post union area" << std::endl; - // std::cout << union_area << "debug" << std::endl; - overlap = inter_area / union_area; - if (overlap >= thresh) suppressed_rw(j) = 1; - poly_union.clear(); - } - } - poly.clear(); - qpoly.clear(); - poly_inter.clear(); - } - } - return keep; -} - -constexpr int const threadsPerBlock = sizeof(unsigned long long) * 8; - -template -int non_max_suppression(py::array_t boxes, py::array_t keep_out, - DType nms_overlap_thresh, int device_id) { - py::buffer_info info = boxes.request(); - auto boxes_ptr = static_cast(info.ptr); - py::buffer_info info_k = keep_out.request(); - auto keep_out_ptr = static_cast(info_k.ptr); - - return _nms_gpu(keep_out_ptr, boxes_ptr, - boxes.shape(0), boxes.shape(1), - nms_overlap_thresh, device_id); -} - -} // namespace spconv -#endif diff --git a/mmdet3d/ops/spconv/include/spconv/nms_functor.h b/mmdet3d/ops/spconv/include/spconv/nms_functor.h deleted file mode 100644 index de88558981..0000000000 --- a/mmdet3d/ops/spconv/include/spconv/nms_functor.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2019 Yan Yan -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef NMS_FUNCTOR_H_ -#define NMS_FUNCTOR_H_ -#include - -namespace spconv { -namespace functor { -template -struct NonMaxSupressionFunctor { - Index operator()(const Device& d, tv::TensorView keep, - tv::TensorView boxes, T threshold, T eps); -}; - -template -struct rotateNonMaxSupressionFunctor { - Index operator()(const Device& d, tv::TensorView keep, - tv::TensorView boxCorners, - tv::TensorView standupIoU, T threshold); -}; - -} // namespace functor -} // namespace spconv - -#endif diff --git a/mmdet3d/ops/spconv/include/spconv/nms_gpu.h b/mmdet3d/ops/spconv/include/spconv/nms_gpu.h deleted file mode 100644 index c17e2ebe93..0000000000 --- a/mmdet3d/ops/spconv/include/spconv/nms_gpu.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2019 Yan Yan -// -// 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. - -#pragma once -template -int _nms_gpu(int *keep_out, const DType *boxes_host, int boxes_num, - int boxes_dim, DType nms_overlap_thresh, int device_id); diff --git a/mmdet3d/ops/spconv/include/spconv/nms_ops.h b/mmdet3d/ops/spconv/include/spconv/nms_ops.h deleted file mode 100644 index dd79b492b7..0000000000 --- a/mmdet3d/ops/spconv/include/spconv/nms_ops.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2019 Yan Yan -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef NMS_TORCH_OP_H_ -#define NMS_TORCH_OP_H_ - -#include -#include -#include -#include -#include -#include -#include - -namespace spconv { -// torch.jit's doc says only support int64, so we need to convert to int32. -template -torch::Tensor -nonMaxSuppression(torch::Tensor boxes, torch::Tensor scores, int64_t preMaxSize, - int64_t postMaxSize, double thresh, double eps) { - // auto timer = spconv::CudaContextTimer<>(); - tv::check_torch_dtype(boxes); - auto resOptions = - torch::TensorOptions().dtype(torch::kInt64).device(boxes.device()); - if (boxes.size(0) == 0){ - return torch::zeros({0}, resOptions); - } - torch::Tensor indices; - if (preMaxSize > 0){ - auto numKeepedScores = scores.size(0); - preMaxSize = std::min(numKeepedScores, preMaxSize); - auto res = torch::topk(scores, preMaxSize); - indices = std::get<1>(res); - boxes = torch::index_select(boxes, 0, indices); - }else{ - indices = std::get<1>(torch::sort(scores)); - boxes = torch::index_select(boxes, 0, indices); - } - if (boxes.size(0) == 0) - return torch::zeros({0}, resOptions); - - auto keep = torch::zeros({boxes.size(0)}, resOptions); - int64_t keepNum = 0; - if (boxes.device().type() == torch::kCPU) { - auto nmsFunctor = functor::NonMaxSupressionFunctor(); - keepNum = nmsFunctor(tv::CPU(), tv::torch2tv(keep), - tv::torch2tv(boxes), T(thresh), T(eps)); - }else{ - TV_ASSERT_RT_ERR(false, "not implemented"); - } - if (postMaxSize <= 0){ - postMaxSize = keepNum; - } - // std::cout << keep << std::endl; - keep = keep.slice(0, 0, std::min(keepNum, postMaxSize)); - if (preMaxSize > 0){ - return torch::index_select(indices, 0, keep); - } - return keep; -} - -} // namespace spconv - -#endif diff --git a/setup.py b/setup.py index 6a51ab3142..d2e9350e57 100644 --- a/setup.py +++ b/setup.py @@ -14,15 +14,6 @@ def readme(): return content -MAJOR = 0 -MINOR = 0 -PATCH = 0 -SUFFIX = 'rc0' -if PATCH != '': - SHORT_VERSION = '{}.{}.{}{}'.format(MAJOR, MINOR, PATCH, SUFFIX) -else: - SHORT_VERSION = '{}.{}{}'.format(MAJOR, MINOR, SUFFIX) - version_file = 'mmdet3d/version.py' @@ -72,12 +63,19 @@ def write_version_py(): # TIME: {} __version__ = '{}' short_version = '{}' +version_info = ({}) """ sha = get_hash() + with open('mmdet3d/VERSION', 'r') as f: + SHORT_VERSION = f.read().strip() + VERSION_INFO = ', '.join( + [x if x.isdigit() else f'"{x}"' for x in SHORT_VERSION.split('.')]) VERSION = SHORT_VERSION + '+' + sha + version_file_str = content.format(time.asctime(), VERSION, SHORT_VERSION, + VERSION_INFO) with open(version_file, 'w') as f: - f.write(content.format(time.asctime(), VERSION, SHORT_VERSION)) + f.write(version_file_str) def get_version():