Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
imagej-elphel
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
3
Issues
3
List
Board
Labels
Milestones
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Elphel
imagej-elphel
Commits
479b64e0
Commit
479b64e0
authored
May 04, 2026
by
Andrey Filippov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: Implement CuasTargetsAnalyze MCP endpoint and fix calcMatchingTargetsLengths bug
parent
ca8ad02a
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
556 additions
and
29 deletions
+556
-29
mcp_compare_targets.py
scripts/mcp_compare_targets.py
+265
-0
Eyesis_Correction.java
.../java/com/elphel/imagej/correction/Eyesis_Correction.java
+11
-10
CuasMotion.java
src/main/java/com/elphel/imagej/cuas/CuasMotion.java
+242
-19
McpServer.java
src/main/java/com/elphel/imagej/mcp/McpServer.java
+38
-0
No files found.
scripts/mcp_compare_targets.py
0 → 100755
View file @
479b64e0
#!/usr/bin/env python3
"""Compare CUAS target sections through the ImageJ MCP HTTP endpoint."""
import
argparse
import
json
import
math
import
sys
from
typing
import
Any
,
Dict
,
Iterable
,
List
,
Mapping
,
Optional
,
Tuple
import
requests
DEFAULT_URL
=
"http://127.0.0.1:48888/mcp/cuas/targets"
PARAMS
:
List
[
Tuple
[
str
,
int
]]
=
[
(
"RSLT_X"
,
0
),
(
"RSLT_Y"
,
1
),
(
"RSLT_VX"
,
16
),
(
"RSLT_VY"
,
17
),
(
"RSLT_VSTR"
,
18
),
(
"RSLT_BX"
,
20
),
(
"RSLT_BY"
,
21
),
(
"RSLT_AX"
,
22
),
(
"RSLT_AY"
,
23
),
(
"RSLT_MISMATCH_BEFORE"
,
24
),
(
"RSLT_MISMATCH_AFTER"
,
25
),
(
"RSLT_MISMATCH_DIRS"
,
26
),
(
"RSLT_MATCH_LENGTH"
,
27
),
(
"RSLT_BEFORE_LENGTH"
,
28
),
(
"RSLT_AFTER_LENGTH"
,
29
),
(
"RSLT_SEQ_TRAVEL"
,
30
),
(
"RSLT_MSCORE"
,
31
),
(
"RSLT_QMATCH"
,
36
),
(
"RSLT_QMATCH_LEN"
,
37
),
(
"RSLT_QTRAVEL"
,
38
),
(
"RSLT_QSCORE"
,
39
),
(
"RSLT_SLOW"
,
41
),
(
"RSLT_FAIL"
,
43
),
(
"RSLT_GLOBAL"
,
48
),
]
PARAM_BY_INDEX
=
{
index
:
name
for
name
,
index
in
PARAMS
}
CORE_FIELDS
=
[
"RSLT_MATCH_LENGTH"
,
"RSLT_BEFORE_LENGTH"
,
"RSLT_AFTER_LENGTH"
,
"RSLT_MISMATCH_BEFORE"
,
"RSLT_MISMATCH_AFTER"
,
"RSLT_SEQ_TRAVEL"
,
"RSLT_QMATCH"
,
"RSLT_QMATCH_LEN"
,
"RSLT_QTRAVEL"
,
"RSLT_QSCORE"
,
"RSLT_GLOBAL"
,
]
TargetKey
=
Tuple
[
int
,
int
,
int
,
int
]
TargetRecord
=
Dict
[
str
,
Any
]
def
parse_args
()
->
argparse
.
Namespace
:
parser
=
argparse
.
ArgumentParser
(
description
=
(
"Query BEFORE and AFTER CUAS target TIFFs through /mcp/cuas/targets "
"and print per-target matching length changes."
)
)
parser
.
add_argument
(
"--file-before"
,
required
=
True
,
help
=
"Path to TARGETS_SINGLE-FIRST TIFF"
)
parser
.
add_argument
(
"--file-after"
,
required
=
True
,
help
=
"Path to MATCHING_LENGTHS2 TIFF"
)
parser
.
add_argument
(
"--tx"
,
type
=
int
,
required
=
True
,
help
=
"Tile X coordinate"
)
parser
.
add_argument
(
"--ty"
,
type
=
int
,
required
=
True
,
help
=
"Tile Y coordinate"
)
parser
.
add_argument
(
"--seq0"
,
type
=
int
,
required
=
True
,
help
=
"First sequence index"
)
parser
.
add_argument
(
"--seq1"
,
type
=
int
,
required
=
True
,
help
=
"Last sequence index"
)
parser
.
add_argument
(
"--tw"
,
type
=
int
,
default
=
1
,
help
=
"Tile-section width; default: 1"
)
parser
.
add_argument
(
"--th"
,
type
=
int
,
default
=
1
,
help
=
"Tile-section height; default: 1"
)
parser
.
add_argument
(
"--url"
,
default
=
DEFAULT_URL
,
help
=
f
"MCP endpoint URL; default: {DEFAULT_URL}"
)
parser
.
add_argument
(
"--timeout"
,
type
=
float
,
default
=
60.0
,
help
=
"HTTP timeout in seconds"
)
parser
.
add_argument
(
"--json"
,
action
=
"store_true"
,
help
=
"Print normalized JSON instead of the text report"
,
)
return
parser
.
parse_args
()
def
query_targets
(
url
:
str
,
target_path
:
str
,
args
:
argparse
.
Namespace
,
)
->
Mapping
[
str
,
Any
]:
payload
=
{
"target_path"
:
target_path
,
"queries"
:
[
{
"mode"
:
"section"
,
"tx"
:
args
.
tx
,
"ty"
:
args
.
ty
,
"tw"
:
args
.
tw
,
"th"
:
args
.
th
,
"nseq0"
:
args
.
seq0
,
"nseq1"
:
args
.
seq1
,
"skip_empty"
:
False
,
"params"
:
[
index
for
_
,
index
in
PARAMS
],
}
],
}
response
=
requests
.
post
(
url
,
json
=
payload
,
timeout
=
args
.
timeout
)
response
.
raise_for_status
()
data
=
response
.
json
()
if
not
data
.
get
(
"ok"
,
False
):
raise
RuntimeError
(
f
"MCP request failed for {target_path}: {data}"
)
return
data
def
normalize_response
(
data
:
Mapping
[
str
,
Any
])
->
Dict
[
TargetKey
,
TargetRecord
]:
results
=
data
.
get
(
"results"
)
or
[]
if
not
results
:
return
{}
section
=
results
[
0
]
if
"error"
in
section
:
raise
RuntimeError
(
str
(
section
[
"error"
]))
records
:
Dict
[
TargetKey
,
TargetRecord
]
=
{}
for
seq
in
section
.
get
(
"sequences"
,
[]):
nseq
=
int
(
seq
[
"nseq"
])
for
tile
in
seq
.
get
(
"tiles"
,
[]):
tx
=
int
(
tile
[
"tx"
])
ty
=
int
(
tile
[
"ty"
])
for
ntarg
,
target
in
enumerate
(
tile
.
get
(
"targets"
,
[])):
named
=
rename_params
(
target
)
named
.
update
({
"nseq"
:
nseq
,
"tx"
:
tx
,
"ty"
:
ty
,
"ntarg"
:
ntarg
})
records
[(
nseq
,
tx
,
ty
,
ntarg
)]
=
named
return
records
def
rename_params
(
target
:
Mapping
[
str
,
Any
])
->
TargetRecord
:
renamed
:
TargetRecord
=
{}
for
key
,
value
in
target
.
items
():
try
:
numeric_key
=
int
(
key
)
except
(
TypeError
,
ValueError
):
renamed
[
key
]
=
value
continue
renamed
[
PARAM_BY_INDEX
.
get
(
numeric_key
,
key
)]
=
value
return
renamed
def
fmt_value
(
value
:
Any
)
->
str
:
if
value
is
None
:
return
"-"
if
isinstance
(
value
,
float
):
if
math
.
isnan
(
value
):
return
"NaN"
if
math
.
isinf
(
value
):
return
"Inf"
if
value
>
0
else
"-Inf"
return
f
"{value:.6g}"
return
str
(
value
)
def
fmt_delta
(
before
:
Any
,
after
:
Any
)
->
str
:
if
before
is
None
or
after
is
None
:
return
""
if
isinstance
(
before
,
(
int
,
float
))
and
isinstance
(
after
,
(
int
,
float
)):
delta
=
after
-
before
if
delta
==
0
:
return
""
return
f
" ({delta:+.6g})"
if
before
!=
after
:
return
" (changed)"
return
""
def
iter_changed_fields
(
before
:
Optional
[
TargetRecord
],
after
:
Optional
[
TargetRecord
])
->
Iterable
[
str
]:
for
field
in
CORE_FIELDS
:
bval
=
before
.
get
(
field
)
if
before
else
None
aval
=
after
.
get
(
field
)
if
after
else
None
if
bval
!=
aval
:
yield
f
"{field}: {fmt_value(bval)} -> {fmt_value(aval)}{fmt_delta(bval, aval)}"
def
print_report
(
before
:
Mapping
[
TargetKey
,
TargetRecord
],
after
:
Mapping
[
TargetKey
,
TargetRecord
],
args
:
argparse
.
Namespace
,
)
->
None
:
keys
=
sorted
(
set
(
before
)
|
set
(
after
))
print
(
f
"Endpoint: {args.url}"
)
print
(
f
"BEFORE: {args.file_before}"
)
print
(
f
"AFTER: {args.file_after}"
)
print
(
f
"Section: tx={args.tx}, ty={args.ty}, tw={args.tw}, th={args.th}, seq={args.seq0}..{args.seq1}"
)
print
(
f
"Targets: before={len(before)}, after={len(after)}, union={len(keys)}"
)
print
()
if
not
keys
:
print
(
"No targets returned for this section."
)
return
for
key
in
keys
:
nseq
,
tx
,
ty
,
ntarg
=
key
btarget
=
before
.
get
(
key
)
atarget
=
after
.
get
(
key
)
status
=
"changed"
if
btarget
is
None
:
status
=
"after-only"
elif
atarget
is
None
:
status
=
"before-only"
elif
not
list
(
iter_changed_fields
(
btarget
,
atarget
)):
status
=
"unchanged"
print
(
f
"nseq={nseq} tile=({tx},{ty}) ntarg={ntarg} [{status}]"
)
print
(
" kinematics:"
)
for
field
in
(
"RSLT_BX"
,
"RSLT_BY"
,
"RSLT_AX"
,
"RSLT_AY"
,
"RSLT_VX"
,
"RSLT_VY"
,
"RSLT_VSTR"
,
"RSLT_SLOW"
,
"RSLT_FAIL"
):
bval
=
btarget
.
get
(
field
)
if
btarget
else
None
aval
=
atarget
.
get
(
field
)
if
atarget
else
None
print
(
f
" {field}: {fmt_value(bval)} -> {fmt_value(aval)}{fmt_delta(bval, aval)}"
)
changed
=
list
(
iter_changed_fields
(
btarget
,
atarget
))
if
changed
:
print
(
" matching:"
)
for
line
in
changed
:
print
(
f
" {line}"
)
else
:
print
(
" matching: no core-field changes"
)
print
()
def
jsonable_records
(
records
:
Mapping
[
TargetKey
,
TargetRecord
])
->
Dict
[
str
,
TargetRecord
]:
return
{
f
"{nseq}:{tx}:{ty}:{ntarg}"
:
record
for
(
nseq
,
tx
,
ty
,
ntarg
),
record
in
sorted
(
records
.
items
())
}
def
main
()
->
int
:
args
=
parse_args
()
try
:
before_raw
=
query_targets
(
args
.
url
,
args
.
file_before
,
args
)
after_raw
=
query_targets
(
args
.
url
,
args
.
file_after
,
args
)
before
=
normalize_response
(
before_raw
)
after
=
normalize_response
(
after_raw
)
except
requests
.
RequestException
as
exc
:
print
(
f
"HTTP error querying MCP endpoint: {exc}"
,
file
=
sys
.
stderr
)
return
2
except
(
ValueError
,
RuntimeError
,
KeyError
,
TypeError
)
as
exc
:
print
(
f
"Error processing MCP response: {exc}"
,
file
=
sys
.
stderr
)
return
3
if
args
.
json
:
print
(
json
.
dumps
(
{
"before"
:
jsonable_records
(
before
),
"after"
:
jsonable_records
(
after
)},
indent
=
2
,
sort_keys
=
True
,
default
=
str
,
)
)
else
:
print_report
(
before
,
after
,
args
)
return
0
if
__name__
==
"__main__"
:
raise
SystemExit
(
main
())
src/main/java/com/elphel/imagej/correction/Eyesis_Correction.java
View file @
479b64e0
...
...
@@ -13503,9 +13503,10 @@ public class Eyesis_Correction implements PlugIn, ActionListener {
String
pluginsDir
=
url
.
substring
(
5
,
url
.
length
()
-
clazz
.
getName
().
length
()
-
6
);
System
.
setProperty
(
"plugins.dir"
,
pluginsDir
);
// start ImageJ
if
(!
GraphicsEnvironment
.
isHeadless
())
{
new
ImageJ
();
}
// run the plugin
IJ
.
runPlugIn
(
clazz
.
getName
(),
""
);
}
}
src/main/java/com/elphel/imagej/cuas/CuasMotion.java
View file @
479b64e0
...
...
@@ -1066,6 +1066,198 @@ public class CuasMotion {
return
vf
;
}
@SuppressWarnings
(
"unchecked"
)
public
static
org
.
json
.
simple
.
JSONArray
targetsAnalyzeMCP
(
String
target_path
,
org
.
json
.
simple
.
JSONArray
queries
)
{
String
[][]
pvf_top_titles
=
new
String
[
1
][];
String
[][]
pvf_titles
=
new
String
[
1
][];
int
[]
err_num
=
new
int
[
1
];
int
[]
wh
=
new
int
[
2
];
double
[][][][]
multi_targets
=
readSingleMultiTargets
(
target_path
,
wh
,
pvf_top_titles
,
pvf_titles
,
err_num
);
org
.
json
.
simple
.
JSONArray
results
=
new
org
.
json
.
simple
.
JSONArray
();
if
(
multi_targets
==
null
)
{
org
.
json
.
simple
.
JSONObject
err
=
new
org
.
json
.
simple
.
JSONObject
();
err
.
put
(
"error"
,
"Failed to read target file. Error code: "
+
err_num
[
0
]);
results
.
add
(
err
);
return
results
;
}
int
tilesX
=
wh
[
0
];
int
tilesY
=
wh
[
1
];
final
int
tileSize
=
GPUTileProcessor
.
DTT_SIZE
;
// 8
final
int
PAR_UPXY
=-
3
,
PAR_VPXY
=-
2
,
PAR_PXY
=-
1
;
for
(
Object
qObj
:
queries
)
{
org
.
json
.
simple
.
JSONObject
q
=
(
org
.
json
.
simple
.
JSONObject
)
qObj
;
org
.
json
.
simple
.
JSONObject
res
=
new
org
.
json
.
simple
.
JSONObject
();
String
modeStr
=
(
String
)
q
.
get
(
"mode"
);
if
(
modeStr
==
null
)
modeStr
=
"section"
;
int
mode
=
0
;
for
(
int
i
=
0
;
i
<
ANLZ_MODES
.
length
;
i
++)
{
if
(
ANLZ_MODES
[
i
].
equals
(
modeStr
))
{
mode
=
i
;
break
;
}
}
long
tx_l
=
q
.
get
(
"tx"
)
!=
null
?
(
Long
)
q
.
get
(
"tx"
)
:
0
;
long
ty_l
=
q
.
get
(
"ty"
)
!=
null
?
(
Long
)
q
.
get
(
"ty"
)
:
0
;
long
tw_l
=
q
.
get
(
"tw"
)
!=
null
?
(
Long
)
q
.
get
(
"tw"
)
:
1
;
long
th_l
=
q
.
get
(
"th"
)
!=
null
?
(
Long
)
q
.
get
(
"th"
)
:
1
;
long
nseq0_l
=
q
.
get
(
"nseq0"
)
!=
null
?
(
Long
)
q
.
get
(
"nseq0"
)
:
0
;
long
nseq1_l
=
q
.
get
(
"nseq1"
)
!=
null
?
(
Long
)
q
.
get
(
"nseq1"
)
:
multi_targets
.
length
-
1
;
int
tx
=
Math
.
max
(
Math
.
min
((
int
)
tx_l
,
tilesX
-
1
),
0
);
int
ty
=
Math
.
max
(
Math
.
min
((
int
)
ty_l
,
tilesY
-
1
),
0
);
int
tw
=
Math
.
max
(
1
,
(
int
)
tw_l
);
int
th
=
Math
.
max
(
1
,
(
int
)
th_l
);
int
nseq0
=
Math
.
max
(
Math
.
min
((
int
)
nseq0_l
,
multi_targets
.
length
-
1
),
0
);
int
nseq1
=
Math
.
max
(
Math
.
min
((
int
)
nseq1_l
,
multi_targets
.
length
-
1
),
nseq0
);
res
.
put
(
"mode"
,
modeStr
);
if
(
mode
==
ANLZ_SECT
)
{
boolean
skip_empty
=
q
.
get
(
"skip_empty"
)
!=
null
?
(
Boolean
)
q
.
get
(
"skip_empty"
)
:
true
;
org
.
json
.
simple
.
JSONArray
paramsJson
=
(
org
.
json
.
simple
.
JSONArray
)
q
.
get
(
"params"
);
int
[]
params
=
new
int
[
paramsJson
!=
null
?
paramsJson
.
size
()
:
1
];
if
(
paramsJson
!=
null
)
{
for
(
int
i
=
0
;
i
<
paramsJson
.
size
();
i
++)
{
params
[
i
]
=
((
Long
)
paramsJson
.
get
(
i
)).
intValue
();
}
}
else
{
params
[
0
]
=
PAR_PXY
;
}
org
.
json
.
simple
.
JSONArray
seqResults
=
new
org
.
json
.
simple
.
JSONArray
();
for
(
int
nseq
=
nseq0
;
nseq
<=
nseq1
;
nseq
++)
{
if
(
multi_targets
[
nseq
]
==
null
)
continue
;
org
.
json
.
simple
.
JSONObject
seqObj
=
new
org
.
json
.
simple
.
JSONObject
();
seqObj
.
put
(
"nseq"
,
nseq
);
seqObj
.
put
(
"title"
,
pvf_titles
[
0
]
!=
null
&&
nseq
<
pvf_titles
[
0
].
length
?
pvf_titles
[
0
][
nseq
]
:
""
);
org
.
json
.
simple
.
JSONArray
tilesArr
=
new
org
.
json
.
simple
.
JSONArray
();
for
(
int
x
=
tx
;
x
<
Math
.
min
(
tx
+
tw
,
tilesX
);
x
++)
{
for
(
int
y
=
ty
;
y
<
Math
.
min
(
ty
+
th
,
tilesY
);
y
++)
{
int
ntile
=
x
+
y
*
tilesX
;
boolean
has_targets
=
(
multi_targets
[
nseq
][
ntile
]
!=
null
)
&&
(
multi_targets
[
nseq
][
ntile
].
length
>
0
);
if
(!
has_targets
&&
skip_empty
)
continue
;
org
.
json
.
simple
.
JSONObject
tileObj
=
new
org
.
json
.
simple
.
JSONObject
();
tileObj
.
put
(
"tx"
,
x
);
tileObj
.
put
(
"ty"
,
y
);
org
.
json
.
simple
.
JSONArray
targsArr
=
new
org
.
json
.
simple
.
JSONArray
();
if
(
has_targets
)
{
for
(
int
ntarg
=
0
;
ntarg
<
multi_targets
[
nseq
][
ntile
].
length
;
ntarg
++)
{
if
(
multi_targets
[
nseq
][
ntile
][
ntarg
]
==
null
)
continue
;
org
.
json
.
simple
.
JSONObject
targObj
=
new
org
.
json
.
simple
.
JSONObject
();
for
(
int
npar
:
params
)
{
switch
(
npar
)
{
case
PAR_UPXY:
targObj
.
put
(
"upx"
,
multi_targets
[
nseq
][
ntile
][
ntarg
][
CuasMotionLMA
.
RSLT_FL_PX
]);
targObj
.
put
(
"upy"
,
multi_targets
[
nseq
][
ntile
][
ntarg
][
CuasMotionLMA
.
RSLT_FL_PY
]);
targObj
.
put
(
"range"
,
multi_targets
[
nseq
][
ntile
][
ntarg
][
CuasMotionLMA
.
RSLT_FL_RANGE
]);
break
;
case
PAR_VPXY:
targObj
.
put
(
"vx"
,
multi_targets
[
nseq
][
ntile
][
ntarg
][
CuasMotionLMA
.
RSLT_VX
]);
targObj
.
put
(
"vy"
,
multi_targets
[
nseq
][
ntile
][
ntarg
][
CuasMotionLMA
.
RSLT_VY
]);
targObj
.
put
(
"vconf"
,
multi_targets
[
nseq
][
ntile
][
ntarg
][
CuasMotionLMA
.
RSLT_VSTR
]);
break
;
case
PAR_PXY:
targObj
.
put
(
"px"
,
tileSize
*
x
+
tileSize
/
2.0
+
multi_targets
[
nseq
][
ntile
][
ntarg
][
CuasMotionLMA
.
RSLT_X
]);
targObj
.
put
(
"py"
,
tileSize
*
y
+
tileSize
/
2.0
+
multi_targets
[
nseq
][
ntile
][
ntarg
][
CuasMotionLMA
.
RSLT_Y
]);
break
;
default
:
if
(
npar
>=
0
&&
npar
<
multi_targets
[
nseq
][
ntile
][
ntarg
].
length
)
{
targObj
.
put
(
String
.
valueOf
(
npar
),
multi_targets
[
nseq
][
ntile
][
ntarg
][
npar
]);
}
}
}
targsArr
.
add
(
targObj
);
}
}
tileObj
.
put
(
"targets"
,
targsArr
);
tilesArr
.
add
(
tileObj
);
}
}
if
(!
tilesArr
.
isEmpty
()
||
!
skip_empty
)
{
seqObj
.
put
(
"tiles"
,
tilesArr
);
seqResults
.
add
(
seqObj
);
}
}
res
.
put
(
"sequences"
,
seqResults
);
}
else
{
// discover modes
int
max_targets
=
q
.
get
(
"max_targets"
)
!=
null
?
((
Long
)
q
.
get
(
"max_targets"
)).
intValue
()
:
50
;
int
par_index
=
q
.
get
(
"par_index"
)
!=
null
?
((
Long
)
q
.
get
(
"par_index"
)).
intValue
()
:
0
;
double
par_val
=
q
.
get
(
"par_val"
)
!=
null
?
((
Number
)
q
.
get
(
"par_val"
)).
doubleValue
()
:
0.0
;
org
.
json
.
simple
.
JSONArray
foundTargets
=
new
org
.
json
.
simple
.
JSONArray
();
int
count
=
0
;
for
(
int
nseq
=
nseq0
;
nseq
<=
nseq1
&&
count
<
max_targets
;
nseq
++)
{
if
(
multi_targets
[
nseq
]
==
null
)
continue
;
for
(
int
x
=
tx
;
x
<
Math
.
min
(
tx
+
tw
,
tilesX
)
&&
count
<
max_targets
;
x
++)
{
for
(
int
y
=
ty
;
y
<
Math
.
min
(
ty
+
th
,
tilesY
)
&&
count
<
max_targets
;
y
++)
{
int
ntile
=
x
+
y
*
tilesX
;
if
(
multi_targets
[
nseq
][
ntile
]
==
null
||
multi_targets
[
nseq
][
ntile
].
length
==
0
)
continue
;
for
(
int
ntarg
=
0
;
ntarg
<
multi_targets
[
nseq
][
ntile
].
length
&&
count
<
max_targets
;
ntarg
++)
{
if
(
multi_targets
[
nseq
][
ntile
][
ntarg
]
==
null
)
continue
;
boolean
match
=
false
;
double
val
=
0
;
if
(
par_index
>=
0
&&
par_index
<
multi_targets
[
nseq
][
ntile
][
ntarg
].
length
)
{
val
=
multi_targets
[
nseq
][
ntile
][
ntarg
][
par_index
];
}
switch
(
mode
)
{
case
ANLZ_NEMPTY:
match
=
true
;
break
;
case
ANLZ_DEFINED:
match
=
!
Double
.
isNaN
(
val
);
break
;
case
ANLZ_EQ:
match
=
(
val
==
par_val
);
break
;
case
ANLZ_GE:
match
=
(
val
>=
par_val
);
break
;
case
ANLZ_LE:
match
=
(
val
<=
par_val
);
break
;
}
if
(
match
)
{
org
.
json
.
simple
.
JSONObject
matchObj
=
new
org
.
json
.
simple
.
JSONObject
();
matchObj
.
put
(
"nseq"
,
nseq
);
matchObj
.
put
(
"tx"
,
x
);
matchObj
.
put
(
"ty"
,
y
);
matchObj
.
put
(
"ntarg"
,
ntarg
);
if
(
mode
!=
ANLZ_NEMPTY
)
{
matchObj
.
put
(
"val"
,
val
);
}
foundTargets
.
add
(
matchObj
);
count
++;
}
}
}
}
}
res
.
put
(
"matches"
,
foundTargets
);
}
results
.
add
(
res
);
}
return
results
;
}
public
static
void
targetsAnalyze
()
{
int
mode
=
0
;
double
par_val
=
0.0
;
...
...
@@ -6317,6 +6509,8 @@ public class CuasMotion {
final
int
num_tiles
=
targets_multi
[
0
].
length
;
final
int
[][][][]
len_ba
=
new
int
[
2
][
num_seq
][
num_tiles
][];
// first index is before/after
final
int
[][][][]
target_grp
=
new
int
[
2
][
num_seq
][
num_tiles
][];
// [ba][nseq][ntile][ntarg] target group (connected), separately for each direction
final
double
[][][][]
best_mismatch_ba
=
new
double
[
2
][
num_seq
][
num_tiles
][];
final
double
[][][][]
best_dirs_ba
=
new
double
[
2
][
num_seq
][
num_tiles
][];
final
Thread
[]
threads
=
ImageDtt
.
newThreadArray
();
final
AtomicInteger
ai
=
new
AtomicInteger
(
0
);
final
AtomicInteger
agrp
=
new
AtomicInteger
(
1
);
...
...
@@ -6354,6 +6548,9 @@ public class CuasMotion {
}
}
target_grp
[
ba
][
fnseq
][
nTile
]
=
new
int
[
targets
.
length
];
best_mismatch_ba
[
ba
][
fnseq
][
nTile
]
=
new
double
[
targets
.
length
];
best_dirs_ba
[
ba
][
fnseq
][
nTile
]
=
new
double
[
targets
.
length
];
Arrays
.
fill
(
best_mismatch_ba
[
ba
][
fnseq
][
nTile
],
Double
.
POSITIVE_INFINITY
);
if
(
first
)
{
for
(
int
ntarg
=
0
;
ntarg
<
targets
.
length
;
ntarg
++)
{
target_grp
[
ba
][
fnseq
][
nTile
][
ntarg
]
=
agrp
.
getAndIncrement
();
// unique group index
...
...
@@ -6377,7 +6574,7 @@ public class CuasMotion {
for
(
int
ntarg_other
=
0
;
ntarg_other
<
targets_other
.
length
;
ntarg_other
++)
{
double
[]
target_other
=
targets_other
[
ntarg_other
];
if
((
target_other
!=
null
)
&&
(
target_other
[
CuasMotionLMA
.
RSLT_FAIL
]==
CuasMotionLMA
.
FAIL_NONE
)){
if
(
len_ba
[
ba
][
fnseq_other
][
ntile_other
][
ntarg_other
]
>
(
lengths
[
ntarg
]
-
1
))
{
// can potentially improve
if
(
len_ba
[
ba
][
fnseq_other
][
ntile_other
][
ntarg_other
]
>
=
(
lengths
[
ntarg
]
-
1
))
{
// can potentially improve
double
other_x
=
target_other
[
CuasMotionLMA
.
RSLT_BX
+
2
*
(
1
-
ba
)];
// opposite direction
double
other_y
=
target_other
[
CuasMotionLMA
.
RSLT_BY
+
2
*
(
1
-
ba
)];
// opposite direction
boolean
other_slow
=
target_other
[
CuasMotionLMA
.
RSLT_SLOW
]
>
0
;
...
...
@@ -6387,8 +6584,26 @@ public class CuasMotion {
double
mismatch_y
=
other_y
-
this_y
;
double
mismatch2
=
mismatch_x
*
mismatch_x
+
mismatch_y
*
mismatch_y
;
if
(
mismatch2
<=
mm2
)
{
// max_mismatch2) {
lengths
[
ntarg
]
=
len_ba
[
ba
][
fnseq_other
][
ntile_other
][
ntarg_other
]
+
1
;
double
mismatch
=
Math
.
sqrt
(
mismatch2
);
int
proposed_len
=
len_ba
[
ba
][
fnseq_other
][
ntile_other
][
ntarg_other
]
+
1
;
if
((
proposed_len
>
lengths
[
ntarg
])
||
((
proposed_len
==
lengths
[
ntarg
])
&&
(
mismatch
<
best_mismatch_ba
[
ba
][
fnseq
][
nTile
][
ntarg
])))
{
int
dir
=
8
;
for
(
int
ndir
=
0
;
ndir
<
TileNeibs
.
DIR_XY
.
length
;
ndir
++)
{
if
((
TileNeibs
.
DIR_XY
[
ndir
][
0
]
==
dx
)
&&
(
TileNeibs
.
DIR_XY
[
ndir
][
1
]
==
dy
))
{
dir
=
ndir
;
break
;
}
}
int
[]
mm_dirs
=
{-
1
,
-
1
};
int
[]
mm_alts
=
{
0
,
0
};
mm_dirs
[
ba
]
=
dir
;
mm_alts
[
ba
]
=
ntarg_other
;
lengths
[
ntarg
]
=
proposed_len
;
target_grp
[
ba
][
fnseq
][
nTile
][
ntarg
]
=
target_grp
[
ba
][
fnseq_other
][
ntile_other
][
ntarg_other
];
// same group
best_mismatch_ba
[
ba
][
fnseq
][
nTile
][
ntarg
]
=
mismatch
;
best_dirs_ba
[
ba
][
fnseq
][
nTile
][
ntarg
]
=
encodeDirs
(
mm_dirs
,
mm_alts
);
// targets are ordered, starting with strongest.
// combine bbox-es
bbox_ba
[
ba
][
fnseq
][
nTile
][
ntarg
]
=
getBbox
(
...
...
@@ -6404,6 +6619,7 @@ public class CuasMotion {
bbox_ba
[
ba
][
fnseq
][
nTile
][
ntarg
][
2
]+
","
+
bbox_ba
[
ba
][
fnseq
][
nTile
][
ntarg
][
3
]+
"}"
);
}
}
}
else
{
if
(
nTile
==
dbg_tile
)
{
System
.
out
.
println
(
"calcMathingTargetsLengths().3 ba="
+
ba
+
", fnseq = "
+
fnseq
+
", nTile="
+
nTile
+
", ntarg="
+
ntarg
+
...
...
@@ -6504,8 +6720,15 @@ public class CuasMotion {
target
[
CuasMotionLMA
.
RSLT_QMATCH_LEN
]
=
Math
.
pow
(
target
[
CuasMotionLMA
.
RSLT_MATCH_LENGTH
],
match_len_pwr
);
// 1.0 if in every scene;
target
[
CuasMotionLMA
.
RSLT_QTRAVEL
]
=
Math
.
max
((
target
[
CuasMotionLMA
.
RSLT_SEQ_TRAVEL
]
-
seq_travel
)/
seq_travel
,
0
);
double
MM_BEFORE
=
target
[
CuasMotionLMA
.
RSLT_MISMATCH_BEFORE
];
double
MM_AFTER
=
target
[
CuasMotionLMA
.
RSLT_MISMATCH_AFTER
];
double
MM_BEFORE
=
best_mismatch_ba
[
0
][
nSeq
][
ntile
][
ntarg
];
double
MM_AFTER
=
best_mismatch_ba
[
1
][
nSeq
][
ntile
][
ntarg
];
int
[]
dirs_before
=
decodeDirs
(
best_dirs_ba
[
0
][
nSeq
][
ntile
][
ntarg
]);
int
[]
dirs_after
=
decodeDirs
(
best_dirs_ba
[
1
][
nSeq
][
ntile
][
ntarg
]);
target
[
CuasMotionLMA
.
RSLT_MISMATCH_BEFORE
]
=
MM_BEFORE
;
target
[
CuasMotionLMA
.
RSLT_MISMATCH_AFTER
]
=
MM_AFTER
;
target
[
CuasMotionLMA
.
RSLT_MISMATCH_DIRS
]
=
encodeDirs
(
new
int
[]
{
dirs_before
[
0
],
dirs_after
[
1
]},
new
int
[]
{
dirs_before
[
2
],
dirs_after
[
3
]});
target
[
CuasMotionLMA
.
RSLT_QMATCH
]
=
0
;
if
(
MM_BEFORE
<=
good_mismatch
)
{
target
[
CuasMotionLMA
.
RSLT_QMATCH
]
+=
Math
.
max
(
0
,(
good_mismatch
-
MM_BEFORE
)/
good_mismatch
);
// 0 .. 1
...
...
src/main/java/com/elphel/imagej/mcp/McpServer.java
View file @
479b64e0
...
...
@@ -105,6 +105,7 @@ public class McpServer {
server
.
createContext
(
"/mcp/fs/glob"
,
new
FsGlobHandler
());
server
.
createContext
(
"/mcp/fs/csvcol"
,
new
FsCsvColHandler
());
server
.
createContext
(
"/mcp/rag/query"
,
new
RagQueryHandler
());
server
.
createContext
(
"/mcp/cuas/targets"
,
new
CuasTargetsHandler
());
server
.
setExecutor
(
null
);
server
.
start
();
if
(
Eyesis_Correction
.
MCP_DEBUG_LEVEL
>=
Eyesis_Correction
.
MINIMAL_DEBUG_MCP
)
{
...
...
@@ -831,6 +832,43 @@ public class McpServer {
}
}
private
class
CuasTargetsHandler
implements
HttpHandler
{
@Override
public
void
handle
(
HttpExchange
exchange
)
throws
IOException
{
try
{
java
.
io
.
InputStream
is
=
exchange
.
getRequestBody
();
java
.
io
.
ByteArrayOutputStream
buffer
=
new
java
.
io
.
ByteArrayOutputStream
();
int
nRead
;
byte
[]
data
=
new
byte
[
1024
];
while
((
nRead
=
is
.
read
(
data
,
0
,
data
.
length
))
!=
-
1
)
{
buffer
.
write
(
data
,
0
,
nRead
);
}
buffer
.
flush
();
String
requestBody
=
new
String
(
buffer
.
toByteArray
(),
StandardCharsets
.
UTF_8
);
org
.
json
.
simple
.
parser
.
JSONParser
parser
=
new
org
.
json
.
simple
.
parser
.
JSONParser
();
org
.
json
.
simple
.
JSONObject
req
=
(
org
.
json
.
simple
.
JSONObject
)
parser
.
parse
(
requestBody
);
String
targetPath
=
(
String
)
req
.
get
(
"target_path"
);
org
.
json
.
simple
.
JSONArray
queries
=
(
org
.
json
.
simple
.
JSONArray
)
req
.
get
(
"queries"
);
if
(
targetPath
==
null
||
queries
==
null
)
{
sendJson
(
exchange
,
400
,
"{\"ok\":false,\"error\":\"Missing target_path or queries\"}"
);
return
;
}
org
.
json
.
simple
.
JSONArray
results
=
com
.
elphel
.
imagej
.
cuas
.
CuasMotion
.
targetsAnalyzeMCP
(
targetPath
,
queries
);
org
.
json
.
simple
.
JSONObject
response
=
new
org
.
json
.
simple
.
JSONObject
();
response
.
put
(
"ok"
,
true
);
response
.
put
(
"results"
,
results
);
sendJson
(
exchange
,
200
,
response
.
toJSONString
());
}
catch
(
Exception
e
)
{
String
detail
=
jsonEscape
(
e
.
getMessage
());
sendJson
(
exchange
,
500
,
"{\"ok\":false,\"error\":\"Failed to process request\",\"detail\":\""
+
detail
+
"\"}"
);
}
}
}
private
static
String
jsonEscape
(
String
value
)
{
if
(
value
==
null
)
{
return
""
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment