Files
tiny_object_detection/tests/test_core.py
T
u52f85420 04bf90c09b feat: 导入 IRSTD 研究代码与文档(小目标检测)
- 新增 irstd 核心包、scripts、tests、configs
- 新增研究文档:literature_review / mathematical_analysis / novelty_matrix / experiment_plan / research_decision / research_log / manuscript_outline
- 新增 evidence、results 数据与审计产物
- 新增 pyproject.toml、requirements、.gitignore
2026-11-01 10:10:33 +00:00

95 lines
5.0 KiB
Python

import itertools,unittest,math
import numpy as np
from irstd.core import *
class CoreTests(unittest.TestCase):
def test_matching_not_greedy(self):
self.assertEqual(len(matching([[1,1],[1,0]])),2)
def test_empty_matching(self):
self.assertEqual(matching(np.zeros((0,2),bool)),[])
self.assertEqual(matching(np.zeros((2,0),bool)),[])
def test_adjacent_targets_conflict(self):
_,selected=exact_mwis([.9,.8],[(0,1)])
self.assertEqual(len(selected),1)
self.assertEqual(len(exact_mwis([.9,.8],[])[1]),2)
def test_invalid_recall_even_with_feasible_target(self):
self.assertEqual(exact_mwis([1.,2.],[(0,1)])[1],[1])
def test_pixel_threshold_splits_false_object(self):
p=np.array([[.9,.6,.9]])
self.assertEqual(len(components(p>=.5)),1)
self.assertEqual(len(components(p>=.8)),2)
def test_matching_deficiency_exhaustive(self):
# Exhaust all 3x2 eligibility graphs and all subsets.
for flat in itertools.product([0,1],repeat=6):
e=np.array(flat).reshape(3,2)
for bits in itertools.product([0,1],repeat=3):
s=np.flatnonzero(bits);fp=len(s)-len(matching(e[s]))
for j in set(range(3))-set(s):
t=np.append(s,j);self.assertLessEqual(fp,len(t)-len(matching(e[t])))
def test_canonical_bound_exhaustive(self):
for flat in itertools.product([0,1],repeat=6):
e=np.array(flat).reshape(3,2);pos={i for i,_ in matching(e)}
for bits in itertools.product([0,1],repeat=3):
s=np.flatnonzero(bits)
self.assertLessEqual(len(s)-len(matching(e[s])),len(set(s)-pos))
def test_surrogate_upper_bound(self):
s=np.array([-100,-1,0,1,100.])
self.assertGreaterEqual(softplus_bound(s,np.ones(5,bool)),(s>=0).sum())
def test_cp_zero_and_all(self):
self.assertAlmostEqual(cp_upper(0,100,.05),1-.05**.01)
self.assertEqual(cp_upper(100,100,.05),1.)
def test_small_calibration_abstains(self):
r=calibrate(np.zeros((80,101),int),np.linspace(0,1,101),64,alpha=.01,risk='any')
self.assertTrue(r['reject_all'])
def test_large_zero_calibration_certifies_any(self):
r=calibrate(np.zeros((1000,2),int),[.1,.9],64,alpha=.01,risk='any')
self.assertFalse(r['reject_all']);self.assertEqual(r['threshold'],.1)
def test_count_and_any_distinct(self):
counts=np.zeros((1000,2),int)
self.assertTrue(calibrate(counts,[.1,.9],64,.01,risk='count')['reject_all'])
self.assertFalse(calibrate(counts,[.1,.9],64,.01,risk='any')['reject_all'])
def test_invalid_calibration(self):
for x in (np.array([[65]]),np.array([[.5]]),np.array([[np.nan]])):
with self.assertRaises(ValueError):calibrate(x,[.5],64)
def test_fixed_sequence_zero_boundary(self):
for n,expected in ((298,True),(299,False)):
r=calibrate(np.zeros((n,101),int),np.linspace(0,1,101),64,.01,risk='any',method='fixed_sequence')
self.assertEqual(r['reject_all'],expected)
self.assertEqual(r['threshold'],0.)
def test_fixed_sequence_stops_without_restart(self):
# Deliberately nonmonotone table: later passing tests must not be inspected.
x=np.zeros((1000,3),int);x[:,1]=1
r=calibrate(x,[.1,.5,.9],64,.01,risk='any',method='fixed_sequence')
self.assertEqual(r['threshold'],.9)
self.assertEqual([v['threshold'] for v in r['tested']],[.9,.5])
def test_fixed_sequence_count_zero_boundary(self):
n=math.ceil(math.log(.05)/math.log1p(-.01/64))
for size,expected in ((n-1,True),(n,False)):
r=calibrate(np.zeros((size,1),int),[.5],64,.01,method='fixed_sequence')
self.assertEqual(r['reject_all'],expected)
def test_fixed_sequence_support_endpoints(self):
x=np.array([[0],[64]])
self.assertTrue(calibrate(x,[.5],64,0,method='fixed_sequence')['reject_all'])
self.assertFalse(calibrate(x,[.5],64,64,method='fixed_sequence')['reject_all'])
def test_pool_cap_and_stability(self):
p=np.zeros((5,5));p[0,0]=.9;p[4,4]=.8
m,x=candidate_pool(p,.1,1);self.assertEqual(len(m),1);self.assertTrue(m[0][0,0])
def test_metrics_one_to_one(self):
g=np.zeros((9,9),bool);g[4,4]=1
a=np.zeros_like(g);a[4,3]=1;b=np.zeros_like(g);b[4,5]=1
r=image_stats([a,b],g);self.assertEqual((r['tp'],r['fp'],r['fn']),(1,1,0))
def test_empty_image_conventions(self):
r=aggregate([image_stats([],np.zeros((4,4),bool))])
self.assertEqual(r['nIoU'],1.);self.assertIsNone(r['Pd']);self.assertEqual(r['FP_per_image'],0.)
def test_path_matches_enumeration(self):
nodes=[[1.,2.],[3.,0.],[.5,2.]];cost=[np.array([[0.,2.],[1.,0.]]),np.array([[0.,1.],[3.,0.]])]
best,path=best_path(nodes,cost)
scores=[(sum(nodes[t][v] for t,v in enumerate(p))-sum(cost[t][p[t],p[t+1]] for t in range(2)),p) for p in itertools.product(range(2),repeat=3)]
self.assertAlmostEqual(best,max(s[0] for s in scores));self.assertEqual(len(path),3)
def test_disconnected_path(self):
self.assertEqual(best_path([[1.],[2.]],[np.array([[np.inf]])]),(-math.inf,[]))
def test_binary_penalty_exactness(self):
w=[1.,2.,3.];edges=[(0,1),(1,2)];lam=4.
values=[(sum(w[i]*b[i] for i in range(3))-lam*sum(b[i]*b[j] for i,j in edges),b) for b in itertools.product([0,1],repeat=3)]
value,b=max(values);self.assertFalse(any(b[i] and b[j] for i,j in edges));self.assertEqual(value,exact_mwis(w,edges)[0])