Skip to content

Commit

Permalink
- Add the github action for publishing the package.
Browse files Browse the repository at this point in the history
- Rename files.
  • Loading branch information
lartpang committed Mar 5, 2021
1 parent 49ae833 commit c6d273a
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 35 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Upload Python Package

on:
release:
types: [created]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_NAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PSS }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2020 MY_
Copyright (c) 2020 lartpang

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Empty file added py_sod_metrics/__init__.py
Empty file.
53 changes: 33 additions & 20 deletions sod_metrics/__init__.py → py_sod_metrics/sod_metrics.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import numpy as np
from scipy.ndimage import convolve, distance_transform_edt as bwdist

__version__ = '1.2.1'
__version__ = "1.2.1"

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


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

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

return dict(gt=(gt_LT, gt_RT, gt_LB, gt_RB),
pred=(pred_LT, pred_RT, pred_LB, pred_RB),
weight=(w1, w2, w3, w4))
return dict(
gt=(gt_LT, gt_RT, gt_LB, gt_RB),
pred=(pred_LT, pred_RT, pred_LB, pred_RB),
weight=(w1, w2, w3, w4),
)

def ssim(self, pred: np.ndarray, gt: np.ndarray) -> float:
h, w = pred.shape
Expand Down Expand Up @@ -272,14 +273,19 @@ def cal_em_with_threshold(self, pred: np.ndarray, gt: np.ndarray, threshold: flo
enhanced_matrix_sum = fg___numel
else:
parts_numel, combinations = self.generate_parts_numel_combinations(
fg_fg_numel=fg_fg_numel, fg_bg_numel=fg_bg_numel,
pred_fg_numel=fg___numel, pred_bg_numel=bg___numel,
fg_fg_numel=fg_fg_numel,
fg_bg_numel=fg_bg_numel,
pred_fg_numel=fg___numel,
pred_bg_numel=bg___numel,
)

results_parts = []
for i, (part_numel, combination) in enumerate(zip(parts_numel, combinations)):
align_matrix_value = 2 * (combination[0] * combination[1]) / \
(combination[0] ** 2 + combination[1] ** 2 + _EPS)
align_matrix_value = (
2
* (combination[0] * combination[1])
/ (combination[0] ** 2 + combination[1] ** 2 + _EPS)
)
enhanced_matrix_value = (align_matrix_value + 1) ** 2 / 4
results_parts.append(enhanced_matrix_value * part_numel)
enhanced_matrix_sum = sum(results_parts)
Expand Down Expand Up @@ -309,22 +315,29 @@ def cal_em_with_cumsumhistogram(self, pred: np.ndarray, gt: np.ndarray) -> np.nd
enhanced_matrix_sum = fg___numel_w_thrs
else:
parts_numel_w_thrs, combinations = self.generate_parts_numel_combinations(
fg_fg_numel=fg_fg_numel_w_thrs, fg_bg_numel=fg_bg_numel_w_thrs,
pred_fg_numel=fg___numel_w_thrs, pred_bg_numel=bg___numel_w_thrs,
fg_fg_numel=fg_fg_numel_w_thrs,
fg_bg_numel=fg_bg_numel_w_thrs,
pred_fg_numel=fg___numel_w_thrs,
pred_bg_numel=bg___numel_w_thrs,
)

results_parts = np.empty(shape=(4, 256), dtype=np.float64)
for i, (part_numel, combination) in enumerate(zip(parts_numel_w_thrs, combinations)):
align_matrix_value = 2 * (combination[0] * combination[1]) / \
(combination[0] ** 2 + combination[1] ** 2 + _EPS)
align_matrix_value = (
2
* (combination[0] * combination[1])
/ (combination[0] ** 2 + combination[1] ** 2 + _EPS)
)
enhanced_matrix_value = (align_matrix_value + 1) ** 2 / 4
results_parts[i] = enhanced_matrix_value * part_numel
enhanced_matrix_sum = results_parts.sum(axis=0)

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

def generate_parts_numel_combinations(self, fg_fg_numel, fg_bg_numel, pred_fg_numel, pred_bg_numel):
def generate_parts_numel_combinations(
self, fg_fg_numel, fg_bg_numel, pred_fg_numel, pred_bg_numel
):
bg_fg_numel = self.gt_fg_numel - fg_fg_numel
bg_bg_numel = pred_bg_numel - bg_fg_numel

Expand All @@ -342,7 +355,7 @@ def generate_parts_numel_combinations(self, fg_fg_numel, fg_bg_numel, pred_fg_nu
(demeaned_pred_fg_value, demeaned_gt_fg_value),
(demeaned_pred_fg_value, demeaned_gt_bg_value),
(demeaned_pred_bg_value, demeaned_gt_fg_value),
(demeaned_pred_bg_value, demeaned_gt_bg_value)
(demeaned_pred_bg_value, demeaned_gt_bg_value),
]
return parts_numel, combinations

Expand Down Expand Up @@ -415,7 +428,7 @@ def matlab_style_gauss2D(self, shape: tuple = (7, 7), sigma: int = 5) -> np.ndar
fspecial('gaussian',[shape],[sigma])
"""
m, n = [(ss - 1) / 2 for ss in shape]
y, x = np.ogrid[-m: m + 1, -n: n + 1]
y, x = np.ogrid[-m : m + 1, -n : n + 1]
h = np.exp(-(x * x + y * y) / (2 * sigma * sigma))
h[h < np.finfo(h.dtype).eps * h.max()] = 0
sumh = h.sum()
Expand Down
29 changes: 29 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# https://github.com/LongTengDao/TOML/

[tool.isort]
# https://pycqa.github.io/isort/docs/configuration/options/
profile = "black"
multi_line_output = 3
filter_files = true
supported_extensions = "py"

[tool.black]
line-length = 99
include = '\.pyi?$'
exclude = '''
/(
\.eggs
| \.git
| \.idea
| \.vscode
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
| output
)/
'''
15 changes: 9 additions & 6 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,23 @@ For related discussion, please see: <https://github.com/DengPingFan/CODToolbox/i
### Download the file as your script

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

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

### Requirements
### Install it as a python package.

```shell
pip install -r requirements.txt
```shell script
pip install pysodmetrics
```

### Examples

* <./tests/test_metrics.py>
* <./tests/test_metrics.py>
* <./tests/metric_recorder.py>

## Thanks

Expand Down
14 changes: 8 additions & 6 deletions readme_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,23 @@ matlab: Smeasure:0.903; wFmeasure:0.558; MAE:0.037; adpEm:0.941; meanEm:0.957; m
### 下载文件为自己的脚本

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

`-nc`: 如果文件存在,就不会下载

注意:`-nc`: 如果文件存在,就不会下载

### 依赖
### 安装成一个包

```shell
pip install -r requirements.txt
```shell script
pip install pysodmetrics
```

### 示例

* <./tests/test_metrics.py>
* <./tests/metric_recorder.py>

## 感谢

Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
numpy~=1.18.0
scipy~=1.5.0
opencv-python~=4.2.0
30 changes: 30 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from setuptools import setup, find_packages

setup(
name="pysodmetrics",
packages=find_packages(),
version="1.2.1",
license="MIT",
description="A simple and efficient implementation of SOD metrics.",
author="lartpang",
author_email="[email protected]",
url="https://github.com/lartpang/PySODMetrics",
keywords=[
"salient object detection",
"saliency detection",
"metric",
"deep learning",
],
install_requires=["scipy>=1.5,<2", "numpy>=1.18,<2"],
classifiers=[
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
],
)
58 changes: 58 additions & 0 deletions tests/metric_recorder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
# @Time : 2021/3/5
# @Author : Lart Pang
# @GitHub : https://github.com/lartpang


import numpy as np

from py_sod_metrics.sod_metrics import Emeasure, Fmeasure, MAE, Smeasure, WeightedFmeasure


class CalTotalMetric(object):
def __init__(self):
self.cal_mae = MAE()
self.cal_fm = Fmeasure()
self.cal_sm = Smeasure()
self.cal_em = Emeasure()
self.cal_wfm = WeightedFmeasure()

def step(self, pred: np.ndarray, gt: np.ndarray, gt_path: str):
assert pred.ndim == gt.ndim and pred.shape == gt.shape, (pred.shape, gt.shape, gt_path)
assert pred.dtype == np.uint8, pred.dtype
assert gt.dtype == np.uint8, gt.dtype

self.cal_mae.step(pred, gt)
self.cal_fm.step(pred, gt)
self.cal_sm.step(pred, gt)
self.cal_em.step(pred, gt)
self.cal_wfm.step(pred, gt)

def get_results(self, bit_width: int = 3) -> dict:
fm = self.cal_fm.get_results()["fm"]
wfm = self.cal_wfm.get_results()["wfm"]
sm = self.cal_sm.get_results()["sm"]
em = self.cal_em.get_results()["em"]
mae = self.cal_mae.get_results()["mae"]
results = {
"Smeasure": sm,
"wFmeasure": wfm,
"MAE": mae,
"adpEm": em["adp"],
"meanEm": em["curve"].mean(),
"maxEm": em["curve"].max(),
"adpFm": fm["adp"],
"meanFm": fm["curve"].mean(),
"maxFm": fm["curve"].max(),
}
results = {name: metric.round(bit_width) for name, metric in results.items()}
return results


if __name__ == '__main__':
cal_total_seg_metrics = CalTotalMetric()
for batch in data_loader:
seg_preds = model(batch)
for seg_pred in seg_preds:
cal_total_seg_metrics.step(seg_pred, mask_array, mask_path)
fixed_seg_results = cal_total_seg_metrics.get_results()
2 changes: 1 addition & 1 deletion tests/test_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

sys.path.append('../')

import sod_metrics as M
import py_sod_metrics.sod_metrics as M

FM = M.Fmeasure()
WFM = M.WeightedFmeasure()
Expand Down

0 comments on commit c6d273a

Please sign in to comment.