Skip to content

Commit c6d273a

Browse files
committed
- Add the github action for publishing the package.
- Rename files.
1 parent 49ae833 commit c6d273a

File tree

11 files changed

+195
-35
lines changed

11 files changed

+195
-35
lines changed

.github/workflows/python-publish.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Upload Python Package
2+
3+
on:
4+
release:
5+
types: [created]
6+
7+
jobs:
8+
deploy:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v2
12+
- name: Set up Python
13+
uses: actions/setup-python@v2
14+
with:
15+
python-version: '3.x'
16+
- name: Install dependencies
17+
run: |
18+
python -m pip install --upgrade pip
19+
pip install setuptools wheel twine
20+
- name: Build and publish
21+
env:
22+
TWINE_USERNAME: ${{ secrets.PYPI_NAME }}
23+
TWINE_PASSWORD: ${{ secrets.PYPI_PSS }}
24+
run: |
25+
python setup.py sdist bdist_wheel
26+
twine upload dist/*

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2020 MY_
3+
Copyright (c) 2020 lartpang
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

py_sod_metrics/__init__.py

Whitespace-only changes.

sod_metrics/__init__.py renamed to py_sod_metrics/sod_metrics.py

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import numpy as np
22
from scipy.ndimage import convolve, distance_transform_edt as bwdist
33

4-
__version__ = '1.2.1'
4+
__version__ = "1.2.1"
55

66
_EPS = 1e-16
77
_TYPE = np.float64
@@ -88,8 +88,7 @@ def get_results(self) -> dict:
8888
changeable_fm = np.mean(np.array(self.changeable_fms, dtype=_TYPE), axis=0)
8989
precision = np.mean(np.array(self.precisions, dtype=_TYPE), axis=0) # N, 256
9090
recall = np.mean(np.array(self.recalls, dtype=_TYPE), axis=0) # N, 256
91-
return dict(fm=dict(adp=adaptive_fm, curve=changeable_fm),
92-
pr=dict(p=precision, r=recall))
91+
return dict(fm=dict(adp=adaptive_fm, curve=changeable_fm), pr=dict(p=precision, r=recall))
9392

9493

9594
class MAE(object):
@@ -149,11 +148,11 @@ def s_object(self, pred: np.ndarray, gt: np.ndarray) -> float:
149148
def region(self, pred: np.ndarray, gt: np.ndarray) -> float:
150149
x, y = self.centroid(gt)
151150
part_info = self.divide_with_xy(pred, gt, x, y)
152-
w1, w2, w3, w4 = part_info['weight']
151+
w1, w2, w3, w4 = part_info["weight"]
153152
# assert np.isclose(w1 + w2 + w3 + w4, 1), (w1 + w2 + w3 + w4, pred.mean(), gt.mean())
154153

155-
pred1, pred2, pred3, pred4 = part_info['pred']
156-
gt1, gt2, gt3, gt4 = part_info['gt']
154+
pred1, pred2, pred3, pred4 = part_info["pred"]
155+
gt1, gt2, gt3, gt4 = part_info["gt"]
157156
score1 = self.ssim(pred1, gt1)
158157
score2 = self.ssim(pred2, gt2)
159158
score3 = self.ssim(pred3, gt3)
@@ -198,9 +197,11 @@ def divide_with_xy(self, pred: np.ndarray, gt: np.ndarray, x, y) -> dict:
198197
# w4 = (h - y) * (w - x) / area
199198
w4 = 1 - w1 - w2 - w3
200199

201-
return dict(gt=(gt_LT, gt_RT, gt_LB, gt_RB),
202-
pred=(pred_LT, pred_RT, pred_LB, pred_RB),
203-
weight=(w1, w2, w3, w4))
200+
return dict(
201+
gt=(gt_LT, gt_RT, gt_LB, gt_RB),
202+
pred=(pred_LT, pred_RT, pred_LB, pred_RB),
203+
weight=(w1, w2, w3, w4),
204+
)
204205

205206
def ssim(self, pred: np.ndarray, gt: np.ndarray) -> float:
206207
h, w = pred.shape
@@ -272,14 +273,19 @@ def cal_em_with_threshold(self, pred: np.ndarray, gt: np.ndarray, threshold: flo
272273
enhanced_matrix_sum = fg___numel
273274
else:
274275
parts_numel, combinations = self.generate_parts_numel_combinations(
275-
fg_fg_numel=fg_fg_numel, fg_bg_numel=fg_bg_numel,
276-
pred_fg_numel=fg___numel, pred_bg_numel=bg___numel,
276+
fg_fg_numel=fg_fg_numel,
277+
fg_bg_numel=fg_bg_numel,
278+
pred_fg_numel=fg___numel,
279+
pred_bg_numel=bg___numel,
277280
)
278281

279282
results_parts = []
280283
for i, (part_numel, combination) in enumerate(zip(parts_numel, combinations)):
281-
align_matrix_value = 2 * (combination[0] * combination[1]) / \
282-
(combination[0] ** 2 + combination[1] ** 2 + _EPS)
284+
align_matrix_value = (
285+
2
286+
* (combination[0] * combination[1])
287+
/ (combination[0] ** 2 + combination[1] ** 2 + _EPS)
288+
)
283289
enhanced_matrix_value = (align_matrix_value + 1) ** 2 / 4
284290
results_parts.append(enhanced_matrix_value * part_numel)
285291
enhanced_matrix_sum = sum(results_parts)
@@ -309,22 +315,29 @@ def cal_em_with_cumsumhistogram(self, pred: np.ndarray, gt: np.ndarray) -> np.nd
309315
enhanced_matrix_sum = fg___numel_w_thrs
310316
else:
311317
parts_numel_w_thrs, combinations = self.generate_parts_numel_combinations(
312-
fg_fg_numel=fg_fg_numel_w_thrs, fg_bg_numel=fg_bg_numel_w_thrs,
313-
pred_fg_numel=fg___numel_w_thrs, pred_bg_numel=bg___numel_w_thrs,
318+
fg_fg_numel=fg_fg_numel_w_thrs,
319+
fg_bg_numel=fg_bg_numel_w_thrs,
320+
pred_fg_numel=fg___numel_w_thrs,
321+
pred_bg_numel=bg___numel_w_thrs,
314322
)
315323

316324
results_parts = np.empty(shape=(4, 256), dtype=np.float64)
317325
for i, (part_numel, combination) in enumerate(zip(parts_numel_w_thrs, combinations)):
318-
align_matrix_value = 2 * (combination[0] * combination[1]) / \
319-
(combination[0] ** 2 + combination[1] ** 2 + _EPS)
326+
align_matrix_value = (
327+
2
328+
* (combination[0] * combination[1])
329+
/ (combination[0] ** 2 + combination[1] ** 2 + _EPS)
330+
)
320331
enhanced_matrix_value = (align_matrix_value + 1) ** 2 / 4
321332
results_parts[i] = enhanced_matrix_value * part_numel
322333
enhanced_matrix_sum = results_parts.sum(axis=0)
323334

324335
em = enhanced_matrix_sum / (self.gt_size - 1 + _EPS)
325336
return em
326337

327-
def generate_parts_numel_combinations(self, fg_fg_numel, fg_bg_numel, pred_fg_numel, pred_bg_numel):
338+
def generate_parts_numel_combinations(
339+
self, fg_fg_numel, fg_bg_numel, pred_fg_numel, pred_bg_numel
340+
):
328341
bg_fg_numel = self.gt_fg_numel - fg_fg_numel
329342
bg_bg_numel = pred_bg_numel - bg_fg_numel
330343

@@ -342,7 +355,7 @@ def generate_parts_numel_combinations(self, fg_fg_numel, fg_bg_numel, pred_fg_nu
342355
(demeaned_pred_fg_value, demeaned_gt_fg_value),
343356
(demeaned_pred_fg_value, demeaned_gt_bg_value),
344357
(demeaned_pred_bg_value, demeaned_gt_fg_value),
345-
(demeaned_pred_bg_value, demeaned_gt_bg_value)
358+
(demeaned_pred_bg_value, demeaned_gt_bg_value),
346359
]
347360
return parts_numel, combinations
348361

@@ -415,7 +428,7 @@ def matlab_style_gauss2D(self, shape: tuple = (7, 7), sigma: int = 5) -> np.ndar
415428
fspecial('gaussian',[shape],[sigma])
416429
"""
417430
m, n = [(ss - 1) / 2 for ss in shape]
418-
y, x = np.ogrid[-m: m + 1, -n: n + 1]
431+
y, x = np.ogrid[-m : m + 1, -n : n + 1]
419432
h = np.exp(-(x * x + y * y) / (2 * sigma * sigma))
420433
h[h < np.finfo(h.dtype).eps * h.max()] = 0
421434
sumh = h.sum()

pyproject.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# https://github.com/LongTengDao/TOML/
2+
3+
[tool.isort]
4+
# https://pycqa.github.io/isort/docs/configuration/options/
5+
profile = "black"
6+
multi_line_output = 3
7+
filter_files = true
8+
supported_extensions = "py"
9+
10+
[tool.black]
11+
line-length = 99
12+
include = '\.pyi?$'
13+
exclude = '''
14+
/(
15+
\.eggs
16+
| \.git
17+
| \.idea
18+
| \.vscode
19+
| \.hg
20+
| \.mypy_cache
21+
| \.tox
22+
| \.venv
23+
| _build
24+
| buck-out
25+
| build
26+
| dist
27+
| output
28+
)/
29+
'''

readme.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,23 @@ For related discussion, please see: <https://github.com/DengPingFan/CODToolbox/i
4242
### Download the file as your script
4343

4444
```shell script
45-
wget -nc -O metrics.py https://raw.githubusercontent.com/lartpang/PySODMetrics/main/sod_metrics/__init__.py
45+
wget -nc -O metrics.py https://raw.githubusercontent.com/lartpang/PySODMetrics/main/py_sod_metrics/sod_metrics.py
46+
# maybe, you need:
47+
pip install -r requirements.txt
4648
```
4749

48-
`-nc`: If the file 'metrics.py' already exists, it cannot be retrieved.
50+
NOTE: `-nc`: If the file 'metrics.py' already exists, it cannot be retrieved.
4951

50-
### Requirements
52+
### Install it as a python package.
5153

52-
```shell
53-
pip install -r requirements.txt
54+
```shell script
55+
pip install pysodmetrics
5456
```
5557

5658
### Examples
5759

58-
* <./tests/test_metrics.py>
60+
* <./tests/test_metrics.py>
61+
* <./tests/metric_recorder.py>
5962

6063
## Thanks
6164

readme_zh.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,23 @@ matlab: Smeasure:0.903; wFmeasure:0.558; MAE:0.037; adpEm:0.941; meanEm:0.957; m
3535
### 下载文件为自己的脚本
3636

3737
```shell script
38-
wget -nc -O metrics.py https://raw.githubusercontent.com/lartpang/PySODMetrics/main/sod_metrics/__init__.py
38+
wget -nc -O metrics.py https://raw.githubusercontent.com/lartpang/PySODMetrics/main/py_sod_metrics/sod_metrics.py
39+
# 或许你还需要:
40+
pip install -r requirements.txt
3941
```
4042

41-
`-nc`: 如果文件存在,就不会下载
42-
43+
注意:`-nc`: 如果文件存在,就不会下载
4344

44-
### 依赖
45+
### 安装成一个包
4546

46-
```shell
47-
pip install -r requirements.txt
47+
```shell script
48+
pip install pysodmetrics
4849
```
4950

5051
### 示例
5152

5253
* <./tests/test_metrics.py>
54+
* <./tests/metric_recorder.py>
5355

5456
## 感谢
5557

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
numpy~=1.18.0
22
scipy~=1.5.0
3-
opencv-python~=4.2.0

setup.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from setuptools import setup, find_packages
2+
3+
setup(
4+
name="pysodmetrics",
5+
packages=find_packages(),
6+
version="1.2.1",
7+
license="MIT",
8+
description="A simple and efficient implementation of SOD metrics.",
9+
author="lartpang",
10+
author_email="[email protected]",
11+
url="https://github.com/lartpang/PySODMetrics",
12+
keywords=[
13+
"salient object detection",
14+
"saliency detection",
15+
"metric",
16+
"deep learning",
17+
],
18+
install_requires=["scipy>=1.5,<2", "numpy>=1.18,<2"],
19+
classifiers=[
20+
"Development Status :: 5 - Production/Stable",
21+
"Environment :: Console",
22+
"Intended Audience :: Developers",
23+
"License :: OSI Approved :: MIT License",
24+
"Operating System :: OS Independent",
25+
"Programming Language :: Python :: 3.7",
26+
"Programming Language :: Python :: 3.8",
27+
"Programming Language :: Python :: 3.9",
28+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
29+
],
30+
)

tests/metric_recorder.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# -*- coding: utf-8 -*-
2+
# @Time : 2021/3/5
3+
# @Author : Lart Pang
4+
# @GitHub : https://github.com/lartpang
5+
6+
7+
import numpy as np
8+
9+
from py_sod_metrics.sod_metrics import Emeasure, Fmeasure, MAE, Smeasure, WeightedFmeasure
10+
11+
12+
class CalTotalMetric(object):
13+
def __init__(self):
14+
self.cal_mae = MAE()
15+
self.cal_fm = Fmeasure()
16+
self.cal_sm = Smeasure()
17+
self.cal_em = Emeasure()
18+
self.cal_wfm = WeightedFmeasure()
19+
20+
def step(self, pred: np.ndarray, gt: np.ndarray, gt_path: str):
21+
assert pred.ndim == gt.ndim and pred.shape == gt.shape, (pred.shape, gt.shape, gt_path)
22+
assert pred.dtype == np.uint8, pred.dtype
23+
assert gt.dtype == np.uint8, gt.dtype
24+
25+
self.cal_mae.step(pred, gt)
26+
self.cal_fm.step(pred, gt)
27+
self.cal_sm.step(pred, gt)
28+
self.cal_em.step(pred, gt)
29+
self.cal_wfm.step(pred, gt)
30+
31+
def get_results(self, bit_width: int = 3) -> dict:
32+
fm = self.cal_fm.get_results()["fm"]
33+
wfm = self.cal_wfm.get_results()["wfm"]
34+
sm = self.cal_sm.get_results()["sm"]
35+
em = self.cal_em.get_results()["em"]
36+
mae = self.cal_mae.get_results()["mae"]
37+
results = {
38+
"Smeasure": sm,
39+
"wFmeasure": wfm,
40+
"MAE": mae,
41+
"adpEm": em["adp"],
42+
"meanEm": em["curve"].mean(),
43+
"maxEm": em["curve"].max(),
44+
"adpFm": fm["adp"],
45+
"meanFm": fm["curve"].mean(),
46+
"maxFm": fm["curve"].max(),
47+
}
48+
results = {name: metric.round(bit_width) for name, metric in results.items()}
49+
return results
50+
51+
52+
if __name__ == '__main__':
53+
cal_total_seg_metrics = CalTotalMetric()
54+
for batch in data_loader:
55+
seg_preds = model(batch)
56+
for seg_pred in seg_preds:
57+
cal_total_seg_metrics.step(seg_pred, mask_array, mask_path)
58+
fixed_seg_results = cal_total_seg_metrics.get_results()

0 commit comments

Comments
 (0)