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
This commit is contained in:
1 parent
9b4a29c4d2
commit
04bf90c09b
35 files changed
+2220
-2
No files matched your search
@@ -0,0 +1,94 @@
|
||||
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])
|
||||
Reference in new issue
Block a user