Skip to content

Commit 87eff30

Browse files
Improved interface and implemented packaging
1 parent c5bb6d6 commit 87eff30

File tree

16 files changed

+476
-143
lines changed

16 files changed

+476
-143
lines changed

.github/workflows/python-publish.yml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: Build and upload to PyPI
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
generate-wheels-matrix:
12+
name: Generate wheels matrix
13+
runs-on: ubuntu-latest
14+
outputs:
15+
include: ${{ steps.set-matrix.outputs.include }}
16+
steps:
17+
- uses: actions/checkout@v3
18+
- name: Install cibuildwheel
19+
run: pipx install cibuildwheel==2.16.2
20+
- id: set-matrix
21+
run: |
22+
MATRIX=$(
23+
{
24+
cibuildwheel --print-build-identifiers --platform linux \
25+
| jq -nRc '{"only": inputs, "os": "ubuntu-latest"}' \
26+
&& \
27+
cibuildwheel --print-build-identifiers --platform macos \
28+
| jq -nRc '{"only": inputs, "os": "macos-latest"}' \
29+
&& cibuildwheel --print-build-identifiers --platform windows \
30+
| jq -nRc '{"only": inputs, "os": "windows-latest"}'
31+
} | jq -sc
32+
)
33+
echo "include=$MATRIX" >> $GITHUB_OUTPUT
34+
env:
35+
CIBW_ARCHS_LINUX: x86_64 i686 aarch64 ppc64le
36+
CIBW_ARCHS_MACOS: x86_64 # arm64 not working due to linker errors
37+
CIBW_ARCHS_WINDOWS: x86 AMD64
38+
CIBW_SKIP: >
39+
pp37-win_amd64 # not working due to compiler errors during numpy installation
40+
pp310-win_amd64 # not working due to missing ndarrayobject.h. Warning from numpy: Numpy built with MINGW-W64 on Windows 64 bits is experimental, and only available for testing.
41+
42+
43+
build_wheels:
44+
name: Build wheels on ${{ matrix.os }}
45+
needs: generate-wheels-matrix
46+
runs-on: ${{ matrix.os }}
47+
strategy:
48+
matrix:
49+
include: ${{ fromJson(needs.generate-wheels-matrix.outputs.include) }}
50+
51+
steps:
52+
- uses: actions/checkout@v4
53+
with:
54+
submodules: recursive
55+
56+
- name: Set up QEMU
57+
if: runner.os == 'Linux'
58+
uses: docker/setup-qemu-action@v3
59+
with:
60+
platforms: all
61+
62+
- name: Build wheels
63+
uses: pypa/[email protected]
64+
with:
65+
only: ${{ matrix.only }}
66+
env:
67+
CIBW_REPAIR_WHEEL_COMMAND_MACOS: ""
68+
69+
- uses: actions/upload-artifact@v3
70+
with:
71+
path: ./wheelhouse/*.whl
72+
73+
build_sdist:
74+
name: Build source distribution
75+
runs-on: ubuntu-latest
76+
steps:
77+
- uses: actions/checkout@v4
78+
with:
79+
submodules: recursive
80+
81+
- name: Build SDist
82+
run: pipx run build --sdist
83+
84+
- uses: actions/upload-artifact@v3
85+
with:
86+
path: dist/*.tar.gz
87+
88+
upload_pypi:
89+
needs: [build_wheels, build_sdist]
90+
runs-on: ubuntu-latest
91+
environment:
92+
name: pypi
93+
url: https://pypi.org/p/stag-python
94+
permissions:
95+
id-token: write
96+
if: github.event_name == 'release' && github.event.action == 'published'
97+
steps:
98+
- uses: actions/download-artifact@v3
99+
with:
100+
name: artifact
101+
path: dist
102+
103+
- uses: pypa/gh-action-pypi-publish@release/v1

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
/lib/
66
/stag_python.egg-info/
77
/cmake-build-debug/
8+
/dist/
9+
/wheelhouse/
810

911
# Compiled Object files
1012
*.slo

CMakeLists.txt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,14 @@ set(CMAKE_MODULE_PATH
2323
${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
2424

2525
find_package(OpenCV 4 REQUIRED)
26-
find_package(Eigen3 3.3 REQUIRED NO_MODULE)
2726
find_package(NumPy REQUIRED)
2827

2928
add_subdirectory(submodules/pybind11)
3029
include_directories("submodules/pybind11/include")
3130
include_directories("submodules/pybind11_opencv_numpy")
3231

33-
pybind11_add_module(stag
34-
StagPy.cpp
32+
pybind11_add_module(_core
33+
src/stag/StagModule.cpp
3534
submodules/pybind11_opencv_numpy/ndarray_converter.cpp
3635
)
3736

@@ -40,6 +39,7 @@ file(GLOB SRC_FILE2 "submodules/stag/src/ED/*.c*")
4039

4140
include_directories(
4241
${OpenCV_INCLUDE_DIRS}
42+
${NUMPY_INCLUDES}
4343
stag/
4444
stag/ED/
4545
)
@@ -54,9 +54,7 @@ target_link_libraries(
5454
)
5555

5656
target_link_libraries(
57-
stag PRIVATE
57+
_core PRIVATE
5858
${OpenCV_LIBS}
5959
staglib
6060
)
61-
62-
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/app)

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 Manfred Stoiber
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 69 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,88 @@
1+
[![Build and upload to PyPI](https://github.com/ManfredStoiber/stag-python/actions/workflows/python-publish.yml/badge.svg)](https://github.com/ManfredStoiber/stag-python/actions/workflows/python-publish.yml)
2+
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/stag-python)
3+
4+
15
# Python Wrapper for [STag - A Stable, Occlusion-Resistant Fiducial Marker System](https://github.com/ManfredStoiber/stag)
26

37
## 📊 Comparison Between Different Marker Systems:
48
[<img src="https://github.com/ManfredStoiber/stag/assets/47210077/668ca457-33dd-4ce7-8b94-662c7a5bb4d9" width="400" height="200" />](https://www.youtube.com/watch?v=vnHI3GzLVrY)
59

6-
## 📋 Getting Started
7-
0. Install Prerequisites
8-
- [CMake >= 3.16](https://cmake.org/getting-started/)
9-
- On Linux: `apt install cmake`
10-
- [OpenCV 4](https://opencv.org/get-started/) for C++
11-
- On Linux: `apt install libopencv-dev`
12-
- [Eigen 3](https://gitlab.com/libeigen/eigen)
13-
- On Linux: `apt install libeigen3-dev`
14-
- Run `pip install numpy`
15-
- On Linux: if during step 2.2 the error "numpy/ndarrayobject.h: No such file or directory" occurs, try one of following solutions:
16-
- Run `apt install python-numpy` or
17-
- Search for "ndarrayobject.h" (`find / -name ndarrayobject.h`) and create a symlink from its parent directory to "/usr/include/numpy" (e.g. `ln -s /usr/local/lib/python3.8/dist-packages/numpy/core/include/numpy /usr/include/numpy`)
18-
- Run `pip install opencv-python`
19-
1. Clone this repository recursively: `git clone --recursive https://github.com/ManfredStoiber/stag-python`
20-
2. Build
21-
1. cd into project: `cd stag-python`
22-
2. `pip install .`
23-
3. Run test app
24-
1. `cd example`
25-
2. `python example.py`
26-
2710
## 📖 Usage
11+
### Installation
12+
`pip install stag-python`
13+
2814

29-
For an example how to use this library, refer to [example.py](https://github.com/ManfredStoiber/stag-python/blob/master/example/example.py)
15+
### Example
16+
Note: in this example cv2 is used for loading the image. To use cv2, you need to install opencv-python: `pip install opencv-python`
17+
```Python
18+
import stag
19+
import cv2
20+
21+
# specify marker type
22+
libraryHD = 21
23+
24+
# load image
25+
image = cv2.imread("example.jpg")
26+
27+
# detect markers
28+
(corners, ids, rejected_corners) = stag.detectMarkers(image, libraryHD)
29+
```
30+
31+
For a more comprehensive example refer to [example.py](https://github.com/ManfredStoiber/stag-python/blob/master/example/example.py)
3032

3133
## 🏷 Markers
3234

33-
- Collection of markers: [Drive](https://drive.google.com/drive/folders/0ByNTNYCAhWbIV1RqdU9vRnd2Vnc?resourcekey=0-9ipvecbezW8EWUva5GBQTQ&usp=sharing)
34-
- Marker Generator: see [ref/marker generator](https://github.com/ManfredStoiber/stag/tree/master/ref/marker%20generator) for reference code for marker generation
35+
- Markers can be downloaded here: [Drive](https://drive.google.com/drive/folders/0ByNTNYCAhWbIV1RqdU9vRnd2Vnc?resourcekey=0-9ipvecbezW8EWUva5GBQTQ&usp=sharing)
36+
- Reference code for Marker Generator: [ref/marker_generator](https://github.com/ManfredStoiber/stag/tree/master/ref/marker_generator)
37+
38+
## 🛠 Configuration
39+
Following parameters can be specified:
40+
- __`libraryHD`__:
41+
- Sets the "family" or "type" of used STag markers
42+
- Each library has a different amount of markers
43+
- Only the markers of the chosen library will be detected
44+
- The following HD libraries are possible:
3545

36-
## 📔 Known Issues
37-
- Sometimes markers are detected multiple times
38-
- Workaround: only use one of the detections
46+
| __HD__ | 11 | 13 | 15 | 17 | 19 | 21 | 23 |
47+
|------------------|--------|-------|-----|-----|----|----|----|
48+
| __Library Size__ | 22,309 | 2,884 | 766 | 157 | 38 | 12 | 6 |
49+
50+
- Specifies the used Hamming Distance, for further information refer to the [original paper](https://arxiv.org/abs/1707.06292)
51+
52+
53+
- __`errorCorrection`__:
54+
- Sets the amount of error correction
55+
- Has to be in range `0 <= errorCorrection <= (libraryHD-1)/2`
56+
- For further information refer to the [original paper](https://arxiv.org/abs/1707.06292)
57+
58+
## 📋 Build From Source
59+
0. __Install__ Prerequisites
60+
61+
[__CMake__ >= 3.16](https://cmake.org/getting-started/)
62+
- On Linux: `apt install cmake`
63+
64+
[__OpenCV__ 4](https://opencv.org/get-started/) for C++
65+
- On Linux: `apt install libopencv-dev`
66+
67+
__NumPy__: `pip install numpy`
68+
- On Linux: if during step 2 the error `"numpy/ndarrayobject.h: No such file or directory"` occurs, try one of following solutions:
69+
- Run `apt install python-numpy` or
70+
- Search for "ndarrayobject.h" (`find / -name ndarrayobject.h`) and create a symlink from its parent directory to "/usr/include/numpy" (e.g. `ln -s /usr/local/lib/python3.8/dist-packages/numpy/core/include/numpy /usr/include/numpy`)
71+
1. __Clone__ this repository recursively:
72+
- `git clone --recursive https://github.com/ManfredStoiber/stag-python`
73+
2. __Build__ the project
74+
75+
In the project directory, run the following command:
76+
77+
- `pip install .`
78+
3. __Run__ the example
79+
1. `cd example`
80+
2. `python example.py`
3981

4082
## 📰 Originally Published in the Following Paper:
4183

4284
[B. Benligiray; C. Topal; C. Akinlar, "STag: A Stable Fiducial Marker System," Image and Vision Computing, 2019.](https://arxiv.org/abs/1707.06292)
4385

44-
Some figures from the paper:
45-
4686
<p align="center">
4787
<img src="https://user-images.githubusercontent.com/19530665/57179654-c0c11e00-6e88-11e9-9ca5-0c0153b28c91.png"/>
4888
</p>

StagPy.cpp

Lines changed: 0 additions & 12 deletions
This file was deleted.

StagWrapper.cpp

Lines changed: 0 additions & 31 deletions
This file was deleted.
File renamed without changes.

example/example.py

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,17 @@
22
import cv2
33
import numpy as np
44

5-
img_path = "testimage.jpg"
6-
img = cv2.imread(img_path)
7-
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
8-
(corners, ids) = stag.detectMarkers(img_gray)
5+
# load image
6+
image = cv2.imread("example.jpg")
97

10-
img_out = img
8+
# detect markers
9+
(corners, ids, rejected_corners) = stag.detectMarkers(image, 21)
1110

12-
for bounding_box, id in zip(corners, ids):
13-
bounding_box = np.round(bounding_box).astype(int)
14-
green = (50, 255, 50)
15-
red = (0, 0, 255)
16-
white = (255, 255, 255)
11+
# draw detected markers with ids
12+
stag.drawDetectedMarkers(image, corners, ids)
1713

18-
center = tuple(np.round(np.mean(bounding_box, axis=0)).astype(int))
19-
20-
# draw white circle at top-left corner
21-
img_out = cv2.circle(img_out, bounding_box[0], 6, white, -1, cv2.LINE_AA)
22-
# draw white border
23-
for i in range(4):
24-
img_out = cv2.line(img_out, tuple(map(int, bounding_box[i])), tuple(map(int, bounding_box[(i+1)%4])), white, 3, cv2.LINE_AA)
25-
26-
# draw green circle at top-left corner
27-
img_out = cv2.circle(img_out, bounding_box[0], 5, green, -1, cv2.LINE_AA)
28-
# draw green border
29-
for i in range(4):
30-
img_out = cv2.line(img_out, tuple(map(int, bounding_box[i])), tuple(map(int, bounding_box[(i+1)%4])), green, 2, cv2.LINE_AA)
31-
32-
# draw white circle at center
33-
img_out = cv2.circle(img_out, center, 6, white, -1, cv2.LINE_AA)
34-
# draw green circle at center
35-
img_out = cv2.circle(img_out, center, 6, green, -1, cv2.LINE_AA)
36-
37-
img_out = cv2.putText(img_out, f"{id}", center, cv2.FONT_HERSHEY_DUPLEX, 2, white, 5, cv2.LINE_AA)
38-
img_out = cv2.putText(img_out, f"{id}", center, cv2.FONT_HERSHEY_DUPLEX, 2, red, 2, cv2.LINE_AA)
39-
40-
41-
cv2.imwrite("testimage_result.jpg", img_out)
42-
print("Detected Corners: ", corners)
43-
print("Detected Ids: ", ids)
44-
print("Results are visualized in testimage_result.jpg")
14+
# draw rejected quads without ids with different color
15+
stag.drawDetectedMarkers(image, rejected_corners, border_color=(255, 0, 0))
4516

17+
# save resulting image
18+
cv2.imwrite("example_result.jpg", image)

example/testimage_result.jpg

-647 KB
Binary file not shown.

0 commit comments

Comments
 (0)