Commit 8946631f authored by Andrey Filippov's avatar Andrey Filippov

Implemented legend, clean up

parent d0583386
......@@ -44,6 +44,7 @@ import ij.process.ImageConverter;
import ij.process.ImageProcessor;
public class CuasMotion {
final public static String CLEAN_SUFFIX = "-CLEAN";
final static public int MAX_ALT_TARGETS = 100; // convenient to be power of 10
final static private int INDX_VX = 0;
......@@ -53,6 +54,13 @@ public class CuasMotion {
final static private int INDX_SPEED = 4; // calculated separately
final static private int INDX_CONFIDENCE = 5; // calculated separately
final static String [] VF_TOP_TITLES = {"vX","vY","strength","fraction","speed", "confidence"};
final static public String LEGEND_TITLE = "Legend";
final static public String[] ICON_DESCRIPTIONS = {
"Identified UAS",
"Unidentified targets",
"UAS flight log position (image pane)",
"UAS flight log position (radar pane)",
"Target detected position (radar pane)"};
final static public int TARGET_X = 0;
final static public int TARGET_Y = 1;
......@@ -97,23 +105,26 @@ public class CuasMotion {
{"diamond21x17.png", "diamond43x35_2px.png"} // foe
};
public static String ICON_BLUE = "Circle63x63blue.png";
public static final String [][] ANNOT_LABELS = {{"Target ID","Unique number fo detected targets, some targets may be erroneously detected as separate ones."},
{"Disparity","Raw measured disparity (in pixels), offset by the same \"disparity at infinity\"."},
{"Range","Distance to the target, calculated from the measured disparity."},
{"True range","Distance to the test UAS obtained from its flight log."},
{"AGL","Altitude above ground level."},
{"Azimuth","Target azimuth (degrees)."},
{"Elevation","Target elevation (degrees)."},
{"Conditional Elevation","Display elevation only if altitude (AGL) is undefined due to too far targets."},
{"Angular movements","Show horizontal and vertical apparent target movement in the camera field of view (degrees per second)."},
{"Vertical Speed","Target vertical speed (m/s)."},
{"Ground Speed","Horizontal target speed relative to the ground (m/s)."},
{"Heading","Target heading (m/s)."},
{"Segment Mismatch","Mismatch between target tracking at consecutive keyframes (now 10 frames @ 60fps) (pix)."},
{"Score","Target 2D detection score."},
{"Sequence Length","Number of consecutive keyframes where the same target is detected."},
{"Travel 2D","Bounding box diagonal of the target 2D travel in consecutive frames (pix)."}};
public static final String OMEGA = "\u03A9";
public static final String [][] ANNOT_LABELS = { // first filed should be 4 character wide
{"ID ", "Target ID", "Unique number of detected targets."}, // , some targets may be erroneously detected as separate ones."},
{"DISP", "Disparity", "Raw measured disparity (in pixels), offset by the same \"disparity at infinity\"."},
{"RNG ", "Range", "Distance to the target, calculated from the measured disparity."},
{"TRNG", "True range","Distance to the test UAS obtained from its flight log."},
{"AGL ", "AGL", "Altitude above ground level."},
{"AZ ", "Azimuth", "Target azimuth (degrees)."},
{"EL ", "Elevation", "Target elevation (degrees)."},
{null, "Conditional Elevation","Display elevation only if altitude (AGL) is undefined due to too far targets."}, // not a label
{OMEGA+"AZ ","Azimuth rotation","Horizontal apparent target movement in the camera field of view (degrees per second)."},
{OMEGA+"EL ","Elevation rotation","Vertical apparent target movement in the camera field of view (degrees per second)."},
{"VS ", "Vertical Speed","Target vertical speed (m/s)."},
{"GS ", "Ground Speed","Horizontal target speed relative to the ground (m/s)."},
{"HDG ","Heading","Target heading (degrees)."},
{"ERRb","Segment Mismatch Before","Mismatch from the previous keyframe (now 10 frames @ 60fps) (pix)."},
{"ERRa","Segment Mismatch After", "Mismatch from the next keyframes (now 10 frames @ 60fps) (pix)."},
{"S ", "Score","Target 2D detection score."},
{"SEQ ", "Sequence Length","Number of consecutive keyframes where the same target is detected."},
{"TRV ", "Travel 2D","Bounding box diagonal of the target 2D travel in consecutive frames (pix)."}};
public static final int ANNOT_ID = 0; // target ID (global index)
public static final int ANNOT_DISP = 1; // disparity
......@@ -123,14 +134,17 @@ public class CuasMotion {
public static final int ANNOT_AZ = 5; // azimuth
public static final int ANNOT_EL = 6; // elevation
public static final int ANNOT_ELNOAGL=7; // elevation if no AGL is available. should be after ANNOT_AGL
public static final int ANNOT_OMEGA = 8; // omega_az,omega_el
public static final int ANNOT_VS = 9; // vertical speed
public static final int ANNOT_GS = 10; // ground speed
public static final int ANNOT_HDG = 11; // heading
public static final int ANNOT_MISM = 12; // Mismatch before/after
public static final int ANNOT_SCORE =13; // Score
public static final int ANNOT_SEQ = 14; // Sequence length
public static final int ANNOT_TRV = 15; // Sequence travel (pixel diagonal of the bounding box of travel)
public static final int ANNOT_OMEGA_AZ = 8; // omega_az
public static final int ANNOT_OMEGA_EL = 9; // omega_el
public static final int ANNOT_VS = 10; // vertical speed
public static final int ANNOT_GS = 11; // ground speed
public static final int ANNOT_HDG = 12; // heading
// public static final int ANNOT_MISM = 13; // Mismatch before/after
public static final int ANNOT_ERRB = 13; // Mismatch before
public static final int ANNOT_ERRA = 14; // Mismatch after
public static final int ANNOT_SCORE =15; // Score
public static final int ANNOT_SEQ = 16; // Sequence length
public static final int ANNOT_TRV = 17; // Sequence travel (pixel diagonal of the bounding box of travel)
/*
public static final int ANNOT_CLEAN =
(1 << ANNOT_ID) |
......@@ -152,11 +166,13 @@ public class CuasMotion {
(1 << ANNOT_AZ) |
(1 << ANNOT_EL) |
(1 << ANNOT_ELNOAGL) |
(1 << ANNOT_OMEGA) |
(1 << ANNOT_OMEGA_AZ) |
(1 << ANNOT_OMEGA_EL) |
(1 << ANNOT_VS) |
(1 << ANNOT_GS) |
(1 << ANNOT_HDG) |
(1 << ANNOT_MISM) |
(1 << ANNOT_ERRB) |
(1 << ANNOT_ERRA) |
(1 << ANNOT_SCORE) |
(1 << ANNOT_SEQ) |
(1 << ANNOT_TRV),
......@@ -3363,7 +3379,7 @@ public class CuasMotion {
/**
* Generate target images
* Generate target images in image pane
* @param clt_parameters
* @param centered target is rendered in the center of the selected tile
* @param mask_width mask width around the target
......@@ -3377,7 +3393,6 @@ public class CuasMotion {
* @param frame_step numer of frames between key frames
* @param velocity_scale velocity scale to apply to vX, vY. It is equal to 1.0/corr_ofset, where corr_offset is disparity in frames
* used for vector field generation
* @param targets60hz target data for each 60hz scene
* @param batch_mode batch mode - disable graphic debugging
* @param debugLevel debug level
* @return float [][] rendered
......@@ -3394,7 +3409,7 @@ public class CuasMotion {
final int frame0,
final int frame_step,
final double velocity_scale, // 1.0/(disparity in frames)
final double [][][] targets60hz,
// final double [][][] targets60hz,
final boolean batch_mode,
final int debugLevel) {
final float [][] fpix_out = new float [background.length][];
......@@ -3521,9 +3536,9 @@ public class CuasMotion {
};
}
ImageDtt.startAndJoin(threads);
if (targets60hz != null) {
targets60hz[nscene] = targets_data;
}
// if (targets60hz != null) {
// targets60hz[nscene] = targets_data;
// }
keyframe = null; // to prevent additional transfers to the GPU
}
}
......@@ -4010,425 +4025,7 @@ public class CuasMotion {
return iclt_fimg[0][0].clone();
}
@Deprecated
public ImagePlus convertToRgbAnnotateTargets_old(
CLTParameters clt_parameters,
final double input_range, // 5
final boolean scale2x,
final int target_type, // = 0; // 0 - unknown, 1 - known, 2 - friend, 3 - foe
final int known_type, // 2; // Target location matching UAS flight log: 0; // 0 - unknown, 1 - known, 2 - friend, 3 - foe
final double known_err, // 20; // Maximal distance between the detected target and UAS log position (in raw image pixels);
final float [][] fpixels,
final double [][][] targets60hz,
final boolean change_color,
final boolean show_disp, // true; // Show disparity (corrected) near target (*** not in clean***)
final boolean show_rng, // true; // Show distance to target (range) in meters
final boolean show_inf, // true; // Show distance greater than max (or negativce) as infinity
final boolean show_inf_gt, // false; // Use ">max" instead of infinity symbol
final boolean show_true_rng,// show true range (from the flight log)
final boolean show_mismatch,
final boolean show_score,
final boolean show_uas,
final int frame0,
final int frame_step,
final int width_src,
final String title,
final String [] titles,
final UasLogReader uasLogReader,
final int debugLevel) {
// parentCLT
final GeometryCorrection gc = parentCLT.getGeometryCorrection();
String uas_log_path = null;
boolean annotate_uas = show_uas && (uasLogReader != null);
if (annotate_uas) {
String resource_name = ICON_BLUE;
URL resourceUrl = CuasMotion.class.getClassLoader().getResource("graphics/"+resource_name);
Path resourcePath = null;
try {
resourcePath = Paths.get(resourceUrl.toURI());
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
uas_log_path = resourcePath.toString();
System.out.println("uas_log_path="+uas_log_path);
}
// preparation of the undetected target icon
String undetected_resource_name = TARGET_ICONS[target_type][scale2x? 1 : 0];
URL undetected_resourceUrl = CuasMotion.class.getClassLoader().getResource("graphics/"+undetected_resource_name);
Path undetected_resourcePath = null;
try {
undetected_resourcePath = Paths.get(undetected_resourceUrl.toURI());
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String undetected_path = undetected_resourcePath.toString();
ColorProcessor undetected_cp = null;
try {
undetected_cp = new ColorProcessor(ImageIO.read(new File(undetected_path)));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
final int undetected_width = undetected_cp.getWidth();
final int undetected_height = undetected_cp.getHeight();
final int [] undetected_pixels = ((int []) undetected_cp.getPixels());
final int [][] undetected_rgba = new int [undetected_pixels.length][4];
for (int i = 0; i < undetected_rgba.length; i++) {
for (int s = 0; s <4; s++) {
undetected_rgba[i][s] = (undetected_pixels[i] >> (8 * s)) & 0xff;
}
}
// preparation of the detected target icon
String detected_resource_name = TARGET_ICONS[known_type][scale2x? 1 : 0];
URL detected_resourceUrl = CuasMotion.class.getClassLoader().getResource("graphics/"+detected_resource_name);
Path detected_resourcePath = null;
try {
detected_resourcePath = Paths.get(detected_resourceUrl.toURI());
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String detected_path = detected_resourcePath.toString();
ColorProcessor detected_cp = null;
try {
detected_cp = new ColorProcessor(ImageIO.read(new File(detected_path)));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
final int detected_width = detected_cp.getWidth();
final int detected_height = detected_cp.getHeight();
final int [] detected_pixels = ((int []) detected_cp.getPixels());
final int [][] detected_rgba = new int [detected_pixels.length][4];
for (int i = 0; i < detected_rgba.length; i++) {
for (int s = 0; s <4; s++) {
detected_rgba[i][s] = (detected_pixels[i] >> (8 * s)) & 0xff;
}
}
final int scale = scale2x? 2 : 1;
final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
final AtomicInteger ai = new AtomicInteger(0);
final boolean annotate_mono = clt_parameters.imp.annotate_mono;
final boolean annotate_transparent_mono = clt_parameters.imp.annotate_transparent_mono;
final Color annotate_color_mono = clt_parameters.imp.annotate_color_mono;
final boolean annotate = clt_parameters.imp.cuas_annotate;
final Color text_color = clt_parameters.imp.cuas_text_color;
final Color text_bg_color = clt_parameters.imp.cuas_text_bg;
final Color selected_color = clt_parameters.imp.cuas_selected_color;
final boolean transparent_other = clt_parameters.imp.cuas_transparent;
final boolean transparent_uas = clt_parameters.imp.cuas_transparent_uas;
final String font_name = clt_parameters.imp.cuas_font_name;
final int font_size = clt_parameters.imp.cuas_font_size;
final int font_type = clt_parameters.imp.cuas_font_type;
final int space_before_text = 2;
final double rng_limit = clt_parameters.imp.cuas_rng_limit;
ColorProcessor uaslog_cp = null;
if (uas_log_path != null) {
try {
uaslog_cp = new ColorProcessor(ImageIO.read(new File(uas_log_path)));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
final int uaslog_width = (uaslog_cp != null) ? uaslog_cp.getWidth() : 0;
final int uaslog_height = (uaslog_cp != null) ? uaslog_cp.getHeight() : 0;
final int [] uaslog_pixels = (uaslog_cp != null) ? ((int []) uaslog_cp.getPixels()) : null;
final int [][] uaslog_rgba = (uaslog_cp != null) ? (new int [uaslog_pixels.length][4]) : null;
if (uaslog_cp != null) {
for (int i = 0; i < uaslog_rgba.length; i++) {
for (int s = 0; s <4; s++) {
uaslog_rgba[i][s] = (uaslog_pixels[i] >> (8 * s)) & 0xff;
}
}
}
if (uaslog_pixels != null) {
System.out.println("uaslog_width="+uaslog_width+",uaslog_height="+uaslog_height);
}
final int num_scenes = fpixels.length;
final int num_pixels = fpixels[0].length;
final int num_seq = targets60hz.length;
ImagePlus imp = ShowDoubleFloatArrays.makeArrays(
fpixels, // float[][] pixels,
width_src, // int width,
num_pixels/width_src, // int height,
title, // String title,
titles); // String [] titles)
imp.getProcessor().setMinAndMax(-input_range/2, input_range/2);
String imp_title = imp.getTitle();
int width_new = scale * imp.getWidth();
int height_new = scale * imp.getHeight();
if (scale2x) {
ImageStack stack = imp.getStack();
String [] labels = new String [stack.getSize()];
for (int i = 0; i < labels.length; i++) {
labels[i] = stack.getSliceLabel(i+1);
}
final FloatProcessor [] stackfp = new FloatProcessor[stack.getSize()];
for (int i = 0; i < stackfp.length; i++) {
stackfp[i] = (FloatProcessor) stack.getProcessor(i+1);
}
System.out.println("stackfp.length="+stackfp.length);
ai.set(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int nSlice = ai.getAndIncrement(); nSlice < stackfp.length; nSlice = ai.getAndIncrement()) {
stackfp[nSlice] = (FloatProcessor) stackfp[nSlice].resize(stackfp[nSlice].getWidth() * 2, stackfp[nSlice].getHeight()*2);
}
}
};
}
ImageDtt.startAndJoin(threads);
stack = new ImageStack(width_new,height_new);
for (int i = 0; i < stackfp.length; i++) {
stack.addSlice(labels[i],stackfp[i]);
}
imp = new ImagePlus(imp_title, stack); // imp.getTitle(),stack);
}
final FloatProcessor [] float_processors = new FloatProcessor[imp.getStack().getSize()];
final ColorProcessor [] color_processors = new ColorProcessor[float_processors.length];
final String [] cp_labels = new String [imp.getStack().getSize()];
for (int i = 0; i < float_processors.length; i++) {
float_processors[i] = (FloatProcessor) imp.getStack().getProcessor(i+1);
cp_labels[i] = imp.getStack().getSliceLabel(i+1);
}
ai.set(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int nSlice = ai.getAndIncrement(); nSlice < float_processors.length; nSlice = ai.getAndIncrement()) {
ImagePlus imp1 = new ImagePlus(cp_labels[nSlice],float_processors[nSlice]);
ImageConverter imageConverter = new ImageConverter(imp1);
imageConverter.convertToRGB();
color_processors[nSlice] = (ColorProcessor) imp1.getProcessor();
}
}
};
}
ImageDtt.startAndJoin(threads);
ImageStack color_stack = new ImageStack(width_new,height_new);
for (int i = 0; i < color_processors.length; i++) {
color_stack.addSlice(cp_labels[i],color_processors[i]);
}
imp = new ImagePlus(imp_title, color_stack); // imp.getTitle(),stack);
final int width = imp.getProcessor().getWidth();
final int height = imp.getProcessor().getHeight();
final Color fcolor = annotate_color_mono;
final ImageStack fstack_scenes = imp.getImageStack();
final int posX= width - scale * 119; // 521;
final int posY= height + scale * 1; // 513;
final Font font = new Font("Monospaced", Font.PLAIN, scale * 12);
final Font font_target = new Font(font_name, font_type, scale * font_size);
final int nSlices = fstack_scenes.getSize();
if (annotate_mono) {
ai.set(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int nSlice = ai.getAndIncrement(); nSlice < nSlices; nSlice = ai.getAndIncrement()) {
String scene_title = fstack_scenes.getSliceLabel(nSlice+1);
ImageProcessor ip = fstack_scenes.getProcessor(nSlice+1);
ip.setColor(fcolor); // Color.BLUE);
ip.setFont(font);
if (annotate_transparent_mono) {
ip.drawString(scene_title, posX, posY); // transparent
} else {
ip.drawString(scene_title, posX, posY, Color.BLACK); // transparent
}
}
}
};
}
ImageDtt.startAndJoin(threads);
}
ImageStack stack = imp.getStack();
int [][] ipixels = new int [num_scenes][];
for (int i = 0; i < num_scenes; i++) {
ipixels[i] = (int[]) stack.getPixels(i+1);
}
// final int num_seq = targets60hz.length;
final int half_step0 = -(frame_step+1)/2;
final int half_step1 = frame_step + half_step0;
ai.set(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int nSeq = ai.getAndIncrement(); nSeq < num_seq; nSeq = ai.getAndIncrement()) {
int frame_center = frame0 + nSeq * frame_step;
for (int dscene = half_step0; dscene < half_step1; dscene ++) {
int nscene = frame_center + dscene;
if ((nscene >=0) && (nscene < num_scenes)) {
double [] uas_pXpYDRange = uasLogReader.getUasPxPyDRange(titles[nscene]);
if (uaslog_rgba != null) { // draw uas from log
// get drone pixel coordinates
int xc = (int) Math.round(scale * uas_pXpYDRange[0]);
int yc = (int) Math.round(scale * uas_pXpYDRange[1]);
int xl = xc - uaslog_width/2;
int yt = yc - uaslog_height/2;
if (nSeq < 0) { // disabled debug, keep 3 to use // 3) {
System.out.println(String.format("uas_pXpYD=[%f,%f,%f]",uas_pXpYDRange[0],uas_pXpYDRange[1],uas_pXpYDRange[2]));
System.out.println(String.format("xc=%d, yc=%d, xl=%d, yt=%d",xc,yc,xl,yt));
System.out.println(String.format("A=%f, T=%f, R=%f",uasLogReader.getCameraATR()[0],uasLogReader.getCameraATR()[1],uasLogReader.getCameraATR()[1]));
System.out.println(String.format("titles[nscene]=%s, startTimeStamp=%f",titles[nscene],uasLogReader.getStartTimestamp()));
}
for (int y = 0; y < uaslog_height; y++) {
int py = yt + y;
if ((py >= 0) && (py < height)) {
for (int x = 0; x< uaslog_width; x++) {
int px = xl +x;
int dpix = x + y * uaslog_width;
int ipix = px + py*width;
if ((px >=0) && (px < width)) {
int alpha = uaslog_rgba[dpix][3];
if (alpha > 0) { // alpha
int dp = uaslog_pixels[x + y * uaslog_width];
if (alpha == 255) {
ipixels[nscene][ipix] = dp;
} else {
double k = alpha/255.0;
int img_pix = ipixels[nscene][ipix];
int new_pix = 0xff000000;
for (int c = 0; c < 3; c++) {
int rgb = (img_pix >> (8 * c)) & 0xff;
rgb = (int) Math.round((1-k)*rgb + k* uaslog_rgba[dpix][c]);
if (rgb > 255) rgb = 255;
new_pix |= (rgb << (8*c));
}
ipixels[nscene][ipix] = new_pix;
}
}
}
}
}
}
}
double [][] targets = targets60hz[nscene];
if (targets != null) { // TODO: find why
int matching_target = findMatchingTarget(
targets, // double [][] targets,
uas_pXpYDRange, // double [] uas_pXpYD,
known_err); // double max_dist)
for (int ntarget = 0; ntarget < targets.length; ntarget++) {
int xc = (int) Math.round(scale * targets[ntarget][TARGET_X]);
int yc = (int) Math.round(scale * targets[ntarget][TARGET_Y]);
int icon_width = (ntarget == matching_target) ? detected_width : undetected_width;
int icon_height = (ntarget == matching_target) ? detected_height : undetected_height;
int [][] target_rgba = (ntarget == matching_target) ? detected_rgba : undetected_rgba;
final int [] target_pixels = (ntarget == matching_target) ? detected_pixels : undetected_pixels;
int xl = xc - icon_width/2;
int yt = yc - icon_height/2;
for (int y = 0; y < icon_height; y++) {
int py = yt + y;
if ((py >= 0) && (py < height)) {
for (int x = 0; x< icon_width; x++) {
int px = xl +x;
int dpix = x + y * icon_width;
int ipix = px + py*width;
if ((px >=0) && (px < width)) {
int alpha = target_rgba[dpix][3];
if (alpha > 0) { // alpha
int dp = target_pixels[x + y * icon_width];
if (alpha == 255) {
ipixels[nscene][ipix] = dp;
} else {
double k = alpha/255.0;
int img_pix = ipixels[nscene][ipix];
int new_pix = 0xff000000;
for (int c = 0; c < 3; c++) {
int rgb = (img_pix >> (8 * c)) & 0xff;
rgb = (int) Math.round((1-k)*rgb + k* target_rgba[dpix][c]);
if (rgb > 255) rgb = 255;
new_pix |= (rgb << (8*c));
}
ipixels[nscene][ipix] = new_pix;
}
}
}
}
}
}
if (annotate) { // TODO: use the same as in radar mode?
int text_left = xl + icon_width + space_before_text * scale;
int text_top = yt + scale * font_size; // text start from the bottom of the first line
ImageProcessor ip = fstack_scenes.getProcessor(nscene+1);
String txt = getTargetText(
clt_parameters, // CLTParameters clt_parameters,
gc, //GeometryCorrection gc,
targets[ntarget]); // double [] target);
if (show_disp) {
double disparity = targets[ntarget][TARGET_DISPARITY];
txt += String.format("\nDISP%4.0f",disparity*1000); // avoid decimal point, show 1000 x disparity
}
double range = targets[ntarget][TARGET_RANGE];
if (show_rng && !Double.isNaN(range) && (range != 0.0)) { // NaN - undefined, 0.0 - undefined
if (show_inf || !Double.isInfinite(range)) {
if (Double.isInfinite(range)) {
if (show_inf_gt) {
txt += String.format("\nRNG>%4.0f",rng_limit);
} else {
txt += "\nRNG \u221E";
}
} else {
txt += String.format("\nRNG %4.0f",range);
}
}
}
// double [][] targets = targets60hz[nscene];
if (show_true_rng && (ntarget == matching_target)) {
// uas_pXpYDRange[3]
txt += String.format("\nTRNG%4.0f",uas_pXpYDRange[3]);
}
if (show_mismatch) {
txt += String.format("\nErr %3.1f\nErr %3.1f",
targets[ntarget][TARGET_MISMATCH_BEFORE],targets[ntarget][TARGET_MISMATCH_AFTER]);
}
if (show_score) {
txt += String.format("\nSEQ %3.1f\nTRV %4.1f\n%5.3f",
targets[ntarget][TARGET_SEQLEN],targets[ntarget][TARGET_TRAVEL], targets[ntarget][TARGET_SCORE]);
}
boolean sel_target = (ntarget == matching_target) && change_color;
ip.setColor(sel_target ? selected_color: text_color);
ip.setFont(font_target);
boolean target_text_transparent = (ntarget == matching_target) ? transparent_uas : transparent_other;
if (target_text_transparent) {
ip.drawString(txt, text_left, text_top); // transparent.
} else {
ip.drawString(txt, text_left, text_top, text_bg_color); // solid color
}
}
}
}
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
return imp;
}
public ImagePlus convertToRgbAnnotateTargets(
CLTParameters clt_parameters,
final boolean clean_pass,
......@@ -4438,15 +4035,8 @@ public class CuasMotion {
final int known_type, // 2; // Target location matching UAS flight log: 0; // 0 - unknown, 1 - known, 2 - friend, 3 - foe
final double known_err, // 20; // Maximal distance between the detected target and UAS log position (in raw image pixels);
final float [][] fpixels,
final double [][][] targets60hz,
/// final double [][][] targets60hz,
final boolean change_color,
final boolean show_disp, // true; // Show disparity (corrected) near target (*** not in clean***)
final boolean show_rng, // true; // Show distance to target (range) in meters
final boolean show_inf, // true; // Show distance greater than max (or negativce) as infinity
final boolean show_inf_gt, // false; // Use ">max" instead of infinity symbol
final boolean show_true_rng,// show true range (from the flight log)
final boolean show_mismatch,
final boolean show_score,
final boolean show_uas,
final int frame0,
final int frame_step,
......@@ -4456,20 +4046,20 @@ public class CuasMotion {
final UasLogReader uasLogReader,
final int debugLevel) {
final boolean annot_missing = clt_parameters.imp.cuas_annot_missing; // false; // make a parameter.Reserve a line for requested but missing parameters
// final boolean clean_pass = false; // (add it)
int annot_mode0 = clt_parameters.imp.cuas_annot_sel[CuasMotion.ANNOT_IMAGE_PANE_INDX];
if (clean_pass) {
annot_mode0 &= clt_parameters.imp.cuas_annot_sel[ANNOT_CLEAN_VIEW_INDX]; //ANNOT_CLEAN;
}
final int annot_mode = annot_mode0;
System.out.println(String.format("Image annotation mode 0x%04x (0x%04x & 0x%04x)",
annot_mode, clt_parameters.imp.cuas_annot_sel[CuasMotion.ANNOT_RADAR_PANE_INDX],clt_parameters.imp.cuas_annot_sel[ANNOT_CLEAN_VIEW_INDX]));
if (debugLevel > -4) {
System.out.println(String.format("Image annotation mode 0x%04x (0x%04x & 0x%04x)",
annot_mode, clt_parameters.imp.cuas_annot_sel[CuasMotion.ANNOT_RADAR_PANE_INDX],clt_parameters.imp.cuas_annot_sel[ANNOT_CLEAN_VIEW_INDX]));
}
// parentCLT
final GeometryCorrection gc = parentCLT.getGeometryCorrection();
String uas_log_path = null;
// final GeometryCorrection gc = parentCLT.getGeometryCorrection();
// String uas_log_path = null;
boolean annotate_uas = show_uas && (uasLogReader != null);
/*
if (annotate_uas) {
String resource_name = ICON_BLUE;
URL resourceUrl = CuasMotion.class.getClassLoader().getResource("graphics/"+resource_name);
......@@ -4483,9 +4073,11 @@ public class CuasMotion {
uas_log_path = resourcePath.toString();
System.out.println("uas_log_path="+uas_log_path);
}
*/
final double [] camera_atr = uasLogReader.getCameraATR();
final ErsCorrection ersCorrection = parentCLT.getErsCorrection();
// prepare UAS log icon (blue circle)
ColorProcessor uaslog_cp = annotate_uas? getUasLogIconColorProcessor():null;
// prepare target icons
ColorProcessor cp_target_icon = getIconColorProcessor(
......@@ -4494,10 +4086,13 @@ public class CuasMotion {
ColorProcessor cp_uas_icon = getIconColorProcessor(
known_type, // uas_type, // int target_type, // Target location matching UAS flight log: 0; // 0 - unknown, 1 - known, 2 - friend, 3 - foe
scale2x); // boolean scale2x)
final int uaslog_width = (uaslog_cp != null) ? uaslog_cp.getWidth() : 0;
final int target_icon_width = cp_target_icon.getWidth();
final int uas_icon_width = cp_uas_icon.getWidth();
final int [] uaslog_pixels = (int[]) uaslog_cp.getPixels();
final int [] target_icon_pixels = (int[]) cp_target_icon.getPixels();
final int [] uas_icon_pixels = (int[]) cp_uas_icon.getPixels();
final int [][] uaslog_rgba = splitColorIcon(uaslog_pixels);
final int [][] target_icon_rgba = splitColorIcon(target_icon_pixels);
final int [][] uas_icon_rgba = splitColorIcon(uas_icon_pixels);
......@@ -4521,40 +4116,10 @@ public class CuasMotion {
final int font_size = clt_parameters.imp.cuas_font_size;
final int font_type = clt_parameters.imp.cuas_font_type;
final int space_before_text = 2 * scale;
final double rng_limit = clt_parameters.imp.cuas_rng_limit;
final double font_ratio = clt_parameters.imp.cuas_font_spacing; // 1.2; // if 0 - will use default spacing ( ~=1.5)
final int tileSize = GPUTileProcessor.DTT_SIZE;
ColorProcessor uaslog_cp = null;
if (uas_log_path != null) {
try {
uaslog_cp = new ColorProcessor(ImageIO.read(new File(uas_log_path)));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
final int uaslog_width = (uaslog_cp != null) ? uaslog_cp.getWidth() : 0;
final int uaslog_height = (uaslog_cp != null) ? uaslog_cp.getHeight() : 0;
final int [] uaslog_pixels = (uaslog_cp != null) ? ((int []) uaslog_cp.getPixels()) : null;
final int [][] uaslog_rgba = (uaslog_cp != null) ? (new int [uaslog_pixels.length][4]) : null;
if (uaslog_cp != null) {
for (int i = 0; i < uaslog_rgba.length; i++) {
for (int s = 0; s <4; s++) {
uaslog_rgba[i][s] = (uaslog_pixels[i] >> (8 * s)) & 0xff;
}
}
}
if (uaslog_pixels != null) {
System.out.println("uaslog_width="+uaslog_width+",uaslog_height="+uaslog_height);
}
final int num_scenes = fpixels.length;
final int num_pixels = fpixels[0].length;
// final int num_seq = targets60hz.length;
final int num_seq = targets.length;
ImagePlus imp = ShowDoubleFloatArrays.makeArrays(
fpixels, // float[][] pixels,
......@@ -4655,10 +4220,6 @@ public class CuasMotion {
for (int i = 0; i < num_scenes; i++) {
ipixels[i] = (int[]) stack.getPixels(i+1);
}
// final int num_seq = targets60hz.length;
final int half_step0 = -(frame_step+1)/2;
final int half_step1 = frame_step + half_step0;
ai.set(0);
final AtomicInteger amax = new AtomicInteger(-1);
final int [] uas_tiles = new int[num_seq];
......@@ -4670,7 +4231,6 @@ public class CuasMotion {
threads[ithread] = new Thread() {
public void run() {
for (int nSeq = ai.getAndIncrement(); nSeq < targets.length; nSeq = ai.getAndIncrement()) {
double [][] targets_seq = targets[nSeq];
/// key - local target_id > 0
/// Key 0 - special case: tiles where UAS log data is stored (may be the same or different than detected tiles
......@@ -4721,14 +4281,10 @@ public class CuasMotion {
color_processors[nscene].setFont(font_target); // to calculate text boxes, later will need to set it for all processors
}
// mark UAS log and targets on the radar
// color_processors[0].setFont(font_target); // to calculate text boxes, later will need to set it for all processors
ai.set(0);
int [] scene_indices = getSceneIndices(
slice_titles, // String [] key_titles, // corresponding to top targets dimension
scene_titles); // String [] scene_titles) { // all titles, one per frame
// final double kpix = radar_height/radar_range;
final Rectangle full_wnd = new Rectangle(0,0,width,height);
ai.set(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
......@@ -4751,41 +4307,16 @@ public class CuasMotion {
double uk = (nscene - nscene0) * kscene; // 0 when nscene=nscene0
double upx = interpolate(uas_target0[CuasMotionLMA.RSLT_FL_PX], uas_target1[CuasMotionLMA.RSLT_FL_PX], uk);
double upy = interpolate(uas_target0[CuasMotionLMA.RSLT_FL_PY], uas_target1[CuasMotionLMA.RSLT_FL_PY], uk);
/// double disparity = interpolate(uas_target0[CuasMotionLMA.RSLT_FL_DISP],uas_target1[CuasMotionLMA.RSLT_FL_DISP],uk);
int xc = (int) Math.round(scale * upx);
int yc = (int) Math.round(scale * upy);
int xl = xc - uaslog_width/2;
int yt = yc - uaslog_height/2;
for (int y = 0; y < uaslog_height; y++) {
int py = yt + y;
if ((py >= 0) && (py < height)) {
for (int x = 0; x< uaslog_width; x++) {
int px = xl +x;
int dpix = x + y * uaslog_width;
int ipix = px + py*width;
if ((px >=0) && (px < width)) {
int alpha = uaslog_rgba[dpix][3];
if (alpha > 0) { // alpha
int dp = uaslog_pixels[x + y * uaslog_width];
if (alpha == 255) {
ipixels[nscene][ipix] = dp;
} else {
double k = alpha/255.0;
int img_pix = ipixels[nscene][ipix];
int new_pix = 0xff000000;
for (int c = 0; c < 3; c++) {
int rgb = (img_pix >> (8 * c)) & 0xff;
rgb = (int) Math.round((1-k)*rgb + k* uaslog_rgba[dpix][c]);
if (rgb > 255) rgb = 255;
new_pix |= (rgb << (8*c));
}
ipixels[nscene][ipix] = new_pix;
}
}
}
}
}
}
imprintPixelIcon(
uaslog_rgba, // int [][] icon, // {R,G,B,A}
uaslog_pixels, // int [] icon_pix,
uaslog_width, // int icon_width,
ipixels[nscene], // int [] image,
width, // int width,
xc, // int xc,
yc); // int yc) {
}
}
}
......@@ -4800,9 +4331,9 @@ public class CuasMotion {
threads[ithread] = new Thread() {
public void run() {
int [] conflict_pix = new int [width * height];
ColorProcessor conflict_cp = new ColorProcessor(width, height, conflict_pix);
/// ColorProcessor conflict_cp = new ColorProcessor(width, height, conflict_pix);
for (int nSeq = ai.getAndIncrement(); nSeq < (num_seq - 1); nSeq = ai.getAndIncrement()) {
int frame_center = frame0 + nSeq * frame_step;
/// int frame_center = frame0 + nSeq * frame_step;
Arrays.fill(conflict_pix, 0);
// for now only for targets longer that 1 series
int nscene0 = scene_indices[nSeq];
......@@ -4860,15 +4391,8 @@ public class CuasMotion {
double [] target1= targets[nSeq+1][ntile1];
double px0 = (ntile0 % tilesX +0.5) * tileSize + target0[CuasMotionLMA.RSLT_X]; // ignoring velocities
double py0 = (ntile0 / tilesX +0.5) * tileSize + target0[CuasMotionLMA.RSLT_Y];
/// double range0= target0[CuasMotionLMA.RSLT_RANGE_LIN];
double px1 = (ntile1 % tilesX +0.5) * tileSize + target1[CuasMotionLMA.RSLT_X];
double py1 = (ntile1 / tilesX +0.5) * tileSize + target1[CuasMotionLMA.RSLT_Y];
/// double range1= target0[CuasMotionLMA.RSLT_RANGE_LIN];
/// double disparity0 = Double.NaN;
/// double disparity1 = Double.NaN;
/// double radar_x0, radar_y0, radar_x1, radar_y1;
/// boolean infinity_mode = false;
// target_coord[ltarget-1] = new double [nscene1-nscene0+1][2];
target_coord[iltarget] = new double [nscene1-nscene0+1][4];
annot_boxes[iltarget] = new Rectangle[nscene1-nscene0+1];
annots[iltarget] = new String[nscene1-nscene0+1];
......@@ -4913,14 +4437,10 @@ public class CuasMotion {
int target_id = target_ids[targets_order[iltarget]];
boolean is_uas = target_id == CuasMultiSeries.TARGET_INDEX_UAS;
boolean target_text_transparent = is_uas ? transparent_uas : transparent_other;
int icon_width = is_uas ? uas_icon_width: target_icon_width;
/// int icon_width = is_uas ? uas_icon_width: target_icon_width;
for (int nscene = nscene0; nscene < nscene1; nscene++) { // threads collide if <=
color_processors[nscene].setColor(is_uas ? selected_color: text_color);
String annot_txt=annots[iltarget][nscene-nscene0];
/*
int text_left = (int)Math.round(target_coord[iltarget][nscene-nscene0][0] + icon_width/2 + space_before_text);
int text_top = (int)Math.round(target_coord[iltarget][nscene-nscene0][1]);
*/
int text_left = (int)Math.round(target_coord[iltarget][nscene-nscene0][2]);
int text_top = (int)Math.round(target_coord[iltarget][nscene-nscene0][3]);
Rectangle abs_box = annot_boxes[iltarget][nscene-nscene0];
......@@ -4995,6 +4515,31 @@ public class CuasMotion {
return cp;
}
public static ColorProcessor getUasLogIconColorProcessor() {
String resource_name = ICON_BLUE;
URL resourceUrl = CuasMotion.class.getClassLoader().getResource("graphics/"+resource_name);
Path resourcePath = null;
try {
resourcePath = Paths.get(resourceUrl.toURI());
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String uas_log_path = resourcePath.toString();
/// System.out.println("uas_log_path="+uas_log_path);
ColorProcessor cp = null;
try {
cp = new ColorProcessor(ImageIO.read(new File(uas_log_path)));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return cp;
}
public static int [][] splitColorIcon(int [] icon){
final int [][] icon_rgba = new int [icon.length][4];
for (int i = 0; i < icon_rgba.length; i++) {
......@@ -5006,7 +4551,7 @@ public class CuasMotion {
}
public static void imprintPixelIcon(
int [][] icon,
int [][] icon, // {R,G,B,A}
int [] icon_pix,
int icon_width,
int [] image,
......@@ -5598,7 +5143,7 @@ public class CuasMotion {
String number_frac_format = "%4.1f";
String number_signed_format = "%3.0f";
String omega_format = "%3.1f";
String omega = "\u03A9";
String sinfinity = "\u221E";
boolean show_inf = clt_parameters.imp.cuas_show_inf; // true; // Show distance greater than max (or negativce) as infinity
boolean show_inf_gt = clt_parameters.imp.cuas_show_inf_gt; // false; // Use ">max" instead of infinity symbol
......@@ -5671,6 +5216,8 @@ public class CuasMotion {
double omega_az_degs = omega_az * 180/Math.PI;
double omega_el_degs = omega_el * 180/Math.PI;
double vel_away = interpolate (target0[CuasMotionLMA.RSLT_VEL_AWAY], target1[CuasMotionLMA.RSLT_VEL_AWAY], k);
if (range > max_axial_range) {
vel_away = 0;
......@@ -5687,24 +5234,30 @@ public class CuasMotion {
StringBuffer sb = new StringBuffer();
if ((annot_mode & (1 << ANNOT_ID)) != 0){
sb.append("ID "+String.format(number_unsigned_format,did)+"\n");
// sb.append("ID "+String.format(number_unsigned_format,did)+"\n");
sb.append(ANNOT_LABELS[ANNOT_ID][0]+String.format(number_unsigned_format,did)+"\n");
}
if ((annot_mode & (1 << ANNOT_DISP)) != 0){
if (!Double.isNaN(disparity)) {
sb.append("DISP"+String.format(number_unsigned_format,disparity*1000)+"\n");
// sb.append("DISP"+String.format(number_unsigned_format,disparity*1000)+"\n");
sb.append(ANNOT_LABELS[ANNOT_DISP][0]+String.format(number_unsigned_format,disparity*1000)+"\n");
} else if (reserve_missing_fields){
sb.append("\n");
}
}
if ((annot_mode & (1 << ANNOT_RANGE)) != 0){
String srng = ANNOT_LABELS[ANNOT_RANGE][0];
if (range <= max_annot_range) { // handles POSITIVE_INFINITY
sb.append("RNG "+String.format(number_unsigned_format,range)+"\n");
sb.append(srng+String.format(number_unsigned_format,range)+"\n");
} else if (show_inf && !Double.isNaN(range)){
if (show_inf_gt) {
sb.append("RNG>"+String.format(number_unsigned_format,max_annot_range)+"\n");
srng = srng.substring(0,3)+">";
// sb.append("RNG>"+String.format(number_unsigned_format,max_annot_range)+"\n");
sb.append(srng+String.format(number_unsigned_format,max_annot_range)+"\n");
} else {
sb.append("RNG "+sinfinity+"\n");
// sb.append("RNG "+sinfinity+"\n");
sb.append(srng+sinfinity+"\n");
}
} else if (reserve_missing_fields){
sb.append("\n");
......@@ -5712,7 +5265,8 @@ public class CuasMotion {
}
if ((annot_mode & (1 << ANNOT_TRANG)) != 0){
if (!Double.isNaN(true_range)) {
sb.append("TRNG"+String.format(number_unsigned_format,true_range)+"\n");
// sb.append("TRNG"+String.format(number_unsigned_format,true_range)+"\n");
sb.append(ANNOT_LABELS[ANNOT_TRANG][0]+String.format(number_unsigned_format,true_range)+"\n");
} else if (reserve_missing_fields){
sb.append("\n");
}
......@@ -5720,7 +5274,8 @@ public class CuasMotion {
boolean has_agl = false;
if ((annot_mode & (1 << ANNOT_AGL)) != 0){
if (!Double.isNaN(agl) && (range <= max_annot_range)) {
sb.append("AGL "+String.format(number_unsigned_format,agl)+"\n");
// sb.append("AGL "+String.format(number_unsigned_format,agl)+"\n");
sb.append(ANNOT_LABELS[ANNOT_AGL][0]+String.format(number_unsigned_format,agl)+"\n");
has_agl = true;
} else if (reserve_missing_fields){
sb.append("\n");
......@@ -5728,72 +5283,129 @@ public class CuasMotion {
}
if ((annot_mode & (1 << ANNOT_AZ)) != 0){
if (!Double.isNaN(az_deg)) {
sb.append("AZM "+String.format(number_unsigned_format,az_deg)+"\n");
// sb.append("AZM "+String.format(number_unsigned_format,az_deg)+"\n");
sb.append(ANNOT_LABELS[ANNOT_AZ][0]+String.format(number_unsigned_format,az_deg)+"\n");
} else if (reserve_missing_fields){
sb.append("\n");
}
}
if (((annot_mode & (1 << ANNOT_EL)) != 0) || (!has_agl && ((annot_mode & (1 << ANNOT_ELNOAGL)) != 0)) ){
if (!Double.isNaN(el_deg)) {
// sb.append("EL "+getSignedDouble(el_deg,omega_format)+"\n");
sb.append("EL "+String.format(number_unsigned_format,el_deg)+"\n");
// sb.append("EL "+String.format(number_unsigned_format,el_deg)+"\n");
sb.append(ANNOT_LABELS[ANNOT_EL][0]+String.format(number_unsigned_format,el_deg)+"\n");
} else if (reserve_missing_fields){
sb.append("\n");
}
}
if ((annot_mode & (1 << ANNOT_OMEGA)) != 0){
sb.append(omega+"AZ "+getSignedDouble(omega_az_degs,omega_format)+"\n");
sb.append(omega+"EL "+getSignedDouble(omega_el_degs,omega_format)+"\n");
if ((annot_mode & (1 << ANNOT_OMEGA_AZ)) != 0){
// sb.append(OMEGA+"AZ "+getSignedDouble(omega_az_degs,omega_format)+"\n");
sb.append(ANNOT_LABELS[ANNOT_OMEGA_AZ][0]+getSignedDouble(omega_az_degs,omega_format)+"\n");
}
if ((annot_mode & (1 << ANNOT_OMEGA_EL)) != 0){
// sb.append(OMEGA+"EL "+getSignedDouble(omega_el_degs,omega_format)+"\n");
sb.append(ANNOT_LABELS[ANNOT_OMEGA_EL][0]+getSignedDouble(omega_el_degs,omega_format)+"\n");
}
if ((annot_mode & (1 << ANNOT_VS)) != 0){
if (!Double.isNaN(vel_up) && (range <= max_annot_range)) {
sb.append("VS "+getSignedDouble(vel_up,omega_format)+"\n");
// sb.append("VS "+getSignedDouble(vel_up,omega_format)+"\n");
sb.append(ANNOT_LABELS[ANNOT_VS][0]+getSignedDouble(vel_up,omega_format)+"\n");
} else if (reserve_missing_fields){
sb.append("\n");
}
}
if ((annot_mode & (1 << ANNOT_GS)) != 0){
if (!Double.isNaN(vel_hor) && (range <= max_annot_range)) {
sb.append("GS "+String.format(number_frac_format,vel_hor)+"\n");
// sb.append(String.format("GS %4.1f\n", vel_hor));
// sb.append("GS "+String.format(number_frac_format,vel_hor)+"\n");
sb.append(ANNOT_LABELS[ANNOT_GS][0]+String.format(number_frac_format,vel_hor)+"\n");
} else if (reserve_missing_fields){
sb.append("\n");
}
}
if ((annot_mode & (1 << ANNOT_HDG)) != 0){
if (!Double.isNaN(hdg_deg) && (range <= max_annot_range)) {
sb.append("HDG "+String.format(number_unsigned_format,hdg_deg)+"\n");
// sb.append(String.format("HDG %4.0f\n", hdg_deg));
// sb.append("HDG "+String.format(number_unsigned_format,hdg_deg)+"\n");
sb.append(ANNOT_LABELS[ANNOT_HDG][0]+String.format(number_unsigned_format,hdg_deg)+"\n");
} else if (reserve_missing_fields){
sb.append("\n");
}
}
if ((annot_mode & (1 << ANNOT_MISM)) != 0){
if ((annot_mode & (1 << ANNOT_ERRB)) != 0){
double mismatch_before = target0[CuasMotionLMA.RSLT_MISMATCH_BEFORE];
// sb.append("ERRb"+String.format(number_frac_format,mismatch_before)+"\n");
sb.append(ANNOT_LABELS[ANNOT_ERRB][0]+String.format(number_frac_format,mismatch_before)+"\n");
}
if ((annot_mode & (1 << ANNOT_ERRA)) != 0){
double mismatch_after = target0[CuasMotionLMA.RSLT_MISMATCH_AFTER];
sb.append("ERRb"+String.format(number_frac_format,mismatch_before)+"\n");
sb.append("ERRa"+String.format(number_frac_format,mismatch_after)+"\n");
// sb.append("ERRa"+String.format(number_frac_format,mismatch_after)+"\n");
sb.append(ANNOT_LABELS[ANNOT_ERRA][0]+String.format(number_frac_format,mismatch_after)+"\n");
}
if ((annot_mode & (1 << ANNOT_SEQ)) != 0){
double sequence_length = target0[CuasMotionLMA.RSLT_MATCH_LENGTH];
sb.append("SEQ "+String.format(number_unsigned_format,sequence_length)+"\n");
// sb.append("SEQ "+String.format(number_unsigned_format,sequence_length)+"\n");
sb.append(ANNOT_LABELS[ANNOT_SEQ][0]+String.format(number_unsigned_format,sequence_length)+"\n");
}
if ((annot_mode & (1 << ANNOT_TRV)) != 0){
double sequence_travel = target0[CuasMotionLMA.RSLT_SEQ_TRAVEL];
sb.append("TRV "+String.format(number_frac_format,sequence_travel)+"\n");
// sb.append("TRV "+String.format(number_frac_format,sequence_travel)+"\n");
sb.append(ANNOT_LABELS[ANNOT_TRV][0]+String.format(number_frac_format,sequence_travel)+"\n");
}
if ((annot_mode & (1 << ANNOT_SCORE)) != 0){
double score = target0[CuasMotionLMA.RSLT_QSCORE];
sb.append("S "+String.format(number_frac_format,score)+"\n");
// sb.append("S "+String.format(number_frac_format,score)+"\n");
sb.append(ANNOT_LABELS[ANNOT_SCORE][0]+String.format(number_frac_format,score)+"\n");
}
// if (!Double.isInfinite(range)) {
return sb.toString();
}
/**
* Approximately calculate azimuth, elevation and angular velocities for far objects
* @param ersCorrection
* @param uasLogReader
* @param fps
* @param px
* @param py
* @param vx
* @param vy
* @return
*/
public static double [][] getPixToAzElev(
ErsCorrection ersCorrection,
UasLogReader uasLogReader,
double fps, // if NaN, will use default 60Hz. Used only for omegas
double px,
double py,
double vx,
double vy) {
if (Double.isNaN(fps)) {
fps = 60.0;
}
double [] camera_atr = uasLogReader.getCameraATR();
double ifov = ersCorrection.getIFOV();
int sensor_width = ersCorrection.getSensorWH()[0];
int sensor_height = ersCorrection.getSensorWH()[1];
double az = Double.NaN;
double el = Double.NaN;
az = (px - sensor_width/2) * ifov + camera_atr[0];
el = - (py - sensor_height/2) * ifov + camera_atr[1];
while (az < 0 ) az += 2*Math.PI;
while (az > 2*Math.PI ) az -= 2*Math.PI;
while (el > Math.PI) el -= 2* Math.PI;
while (el < -Math.PI) el += 2* Math.PI;
double az_deg = az * 180/Math.PI;
double el_deg = el * 180/Math.PI;
double omega_az = vx * ifov * fps;
double omega_el = -vy * ifov * fps;
double omega_az_degs = omega_az * 180/Math.PI;
double omega_el_degs = omega_el * 180/Math.PI;
return new double [][] {{az_deg,el_deg},{omega_az_degs,omega_el_degs}};
}
public static double interpolate (
double v0,
......@@ -6030,7 +5642,7 @@ public class CuasMotion {
return; // imp_new;
}
//
private static int findMatchingTarget(
double [][] targets,
double [] uas_pXpYD,
......@@ -6061,64 +5673,6 @@ public class CuasMotion {
return s+ String.format(format, Math.abs(d));
}
public static String getTargetText(
CLTParameters clt_parameters,
GeometryCorrection gc,
double [] target) {
double [][] az_el_oaz_oel= getPixToAzElev(
clt_parameters, // CLTParameters clt_parameters,
gc, // GeometryCorrection gc,
target[TARGET_X], // double target_x,
target[TARGET_Y], // double target_y,
target[TARGET_VX], // double target_vx,
target[TARGET_VY]); // double target_vy);
String number_format = "%3.0f";
String omega_format = "%3.1f";
String omega = "\u03A9";
String txt = "";
txt += " ID "+String.format("%03d",(int) Math.round(target[TARGET_ID]))+"\n";
txt += " AZ "+String.format(number_format,az_el_oaz_oel[0][0])+"\n";
txt += " EL "+ getSignedDouble(az_el_oaz_oel[0][1],number_format)+"\n";
txt += omega+"AZ "+getSignedDouble(az_el_oaz_oel[1][0],omega_format)+"\n";
txt += omega+"EL "+getSignedDouble(az_el_oaz_oel[1][1],omega_format);
return txt;
}
// TODO: improve, get correct calculations with distortions
public static double [][] getPixToAzElev(
CLTParameters clt_parameters,
GeometryCorrection gc,
double target_x,
double target_y,
double target_vx,
double target_vy) {
double ifov = gc.getIFOVDegrees(); // clt_parameters.imp.cuas_ifov; // 0.05; // degree per pixel
int px0 = clt_parameters.imp.cuas_px0; // 283; // pixel with known azimuth
int py0 = clt_parameters.imp.cuas_py0; // 386; // pixel with known elevation
double az0 = clt_parameters.imp.cuas_az0; // 201.5; // degrees for cuas_px0;
double el0 = clt_parameters.imp.cuas_el0; // 0.0; // degrees for cuas_px0;
double fps = 60.0;
double az = (target_x - px0)*ifov+az0;
double el = -(target_y - py0)*ifov+el0;
double omega_az = target_vx * ifov * fps;
double omega_el = -target_vy * ifov * fps;
while (az < 0) {
az += 360;
}
while (az >= 360) {
az -= 360;
}
while (el < -180) {
el += 360;
}
while (el > 180) {
el -= 360;
}
return new double [][] {{az,el},{omega_az, omega_el}};
}
public static String saveAsVideo(
......@@ -6247,37 +5801,14 @@ public class CuasMotion {
boolean intermed_high = clt_parameters.imp.cuas_intermed_high; // true;
boolean save_mono = clt_parameters.imp.cuas_save_mono; // true;
boolean save_color = clt_parameters.imp.cuas_save_color; // true;
boolean save_video = clt_parameters.imp.cuas_save_video; // true;
boolean target_debug = clt_parameters.imp.cuas_target_debug; // true;
boolean show_target_score = clt_parameters.imp.cuas_target_score; // false; // show target score and sequence length in the final video
boolean save_color0 = clt_parameters.imp.cuas_save_color; // true;
boolean save_video0 = clt_parameters.imp.cuas_save_video; // && video_pass; // true;
boolean annotate_uas = clt_parameters.imp.cuas_annotate_uas; // false; // show circle around UAS position from the flight log
boolean show_target_color = clt_parameters.imp.cuas_target_color; // false; // show target score and sequence length in the final video
boolean show_disp = clt_parameters.imp.cuas_show_disp; // true; // Show disparity (corrected) near target (*** not in clean***)
boolean show_rng = clt_parameters.imp.cuas_show_rng; // true; // Show distance to target (range) in meters
boolean show_inf = clt_parameters.imp.cuas_show_inf; // true; // Show distance greater than max (or negativce) as infinity
boolean show_inf_gt = clt_parameters.imp.cuas_show_inf_gt; // false; // Use ">max" instead of infinity symbol
boolean show_true_rng = clt_parameters.imp.cuas_show_true_rng; // show true range (from the flight log)
double radar_range = clt_parameters.imp.cuas_radar_range; // maximal radar range in meters
String clean_suffix = "";
if (clean_video) {
if (video_pass) {
annotate_uas = false;
save_mono = false;
save_color = false;
target_debug = false;
show_target_score = false;
show_target_color = false;
show_disp = false;
clean_suffix = "-CLEAN";
} else {
save_video = false;
}
}
boolean cuas_gaussian_ra = clt_parameters.imp.cuas_gaussian_ra; // use temporal Gaussian instead of running average
boolean center_targ = false; // clt_parameters.imp.cuas_center_targ;
boolean save_video = save_video0 && video_pass;
boolean save_color = save_color0 && !save_video;
int start_frame = 0;
int seq_length = corr_offset + corr_pairs;
......@@ -6329,7 +5860,7 @@ public class CuasMotion {
parentCLT.saveImagePlusInModelDirectory(imp_accumulated5x5); // ImagePlus imp)
}
double velocity_scale = 1.0/corr_offset;
double [][][] targets60hz = new double [fpixels.length][][];
// double [][][] targets60hz = new double [fpixels.length][][];
float [][] background = fpixels;
String ra_bg_suffix=(ra_background? ("-RABG"+corr_pairs):"");
if (ra_background) {
......@@ -6357,7 +5888,7 @@ public class CuasMotion {
frame0, // final int frame0,
corr_inc, // final int frame_step,
velocity_scale, // final double velocity_scale, // 1.0/(disparity in frames)
targets60hz, // final double [][][] targets60hz,
/// targets60hz, // final double [][][] targets60hz,
batch_mode, // final boolean batch_mode,
debugLevel); // final int debugLevel)
if (save_mono) {
......@@ -6374,25 +5905,19 @@ public class CuasMotion {
}
parentCLT.saveImagePlusInModelDirectory(imp_replaced_targets); // ImagePlus imp)
}
boolean clean_pass = clean_video && video_pass;
String clean_suffix = clean_pass? CLEAN_SUFFIX : "";
ImagePlus imp_color = convertToRgbAnnotateTargets(
clt_parameters, // CLTParameters clt_parameters,
video_pass, // final boolean clean_pass,
clean_pass, // final boolean clean_pass,
input_range, // final double input_range, // 5
scale2x, // boolean scale2x,
target_type, // final int target_type, // = 0; // 0 - unknown, 1 - known, 2 - friend, 3 - foe
known_type, // final int known_type, // 2; // Target location matching UAS flight log: 0; // 0 - unknown, 1 - known, 2 - friend, 3 - foe
known_err, // final double known_err, // 20; // Maximal distance between the detected target and UAS log position (in raw image pixels);
replaced_targets, // final float [][] fpixels,
targets60hz, // final double [][][] targets60hz,
// targets60hz, // final double [][][] targets60hz,
show_target_color, // final boolean change_color,
show_disp, // final boolean show_disp, // true; // Show disparity (corrected) near target (*** not in clean***)
show_rng, // final boolean show_rng, // true; // Show distance to target (range) in meters
show_inf, // final boolean show_inf, // true; // Show distance greater than max (or negativce) as infinity
show_inf_gt, // final boolean show_inf_gt, // false; // Use ">max" instead of infinity symbol
show_true_rng, // final boolean show_true_rng,// show true range (from the flight log)
target_debug, // final boolean show_mismatch,
show_target_score, // final boolean show_score,
annotate_uas, // final boolean show_uas,
frame0, // final int frame0,
corr_inc, // final int frame_step,
......@@ -6407,22 +5932,20 @@ public class CuasMotion {
int height2 = imp_color.getHeight();
int width2 = (int) (16.0/9.0*height2);
int offset_x = width2 - imp_color.getWidth();
// ImagePlus imp_color2
imp_color= increaseCanvas(
imp_color, // ImagePlus imp, // color
width2, // int new_width,
height2, // int new_height,
offset_x, // int x0,
0); // int y0)
// imp_color2.show();
// parentCLT.saveImagePlusInModelDirectory(imp_color2);
// int annot_mode = 0xffffffbf;
int annot_mode = clt_parameters.imp.cuas_annot_sel[CuasMotion.ANNOT_RADAR_PANE_INDX];
if (video_pass) {
annot_mode &= clt_parameters.imp.cuas_annot_sel[ANNOT_CLEAN_VIEW_INDX]; //ANNOT_CLEAN;
}
System.out.println(String.format("Radar annotation mode 0x%04x (0x%04x & 0x%04x)",
annot_mode, clt_parameters.imp.cuas_annot_sel[CuasMotion.ANNOT_RADAR_PANE_INDX],clt_parameters.imp.cuas_annot_sel[ANNOT_CLEAN_VIEW_INDX]));
if (debugLevel > -4) {
System.out.println(String.format("Radar annotation mode 0x%04x (0x%04x & 0x%04x)",
annot_mode, clt_parameters.imp.cuas_annot_sel[CuasMotion.ANNOT_RADAR_PANE_INDX],clt_parameters.imp.cuas_annot_sel[ANNOT_CLEAN_VIEW_INDX]));
}
ImagePlus img_radar = generateRadarImage(
clt_parameters,
annot_mode, // -1, // int annot_mode, // specify bits
......
package com.elphel.imagej.cuas;
import java.awt.Color;
import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
......@@ -16,12 +19,14 @@ import java.util.concurrent.atomic.AtomicInteger;
import com.elphel.imagej.cameras.CLTParameters;
import com.elphel.imagej.gpu.GPUTileProcessor;
import com.elphel.imagej.ims.UasLogReader;
import com.elphel.imagej.tileprocessor.ErsCorrection;
import com.elphel.imagej.tileprocessor.GeometryCorrection;
import com.elphel.imagej.tileprocessor.ImageDtt;
import com.elphel.imagej.tileprocessor.QuadCLT;
import ij.ImagePlus;
import ij.io.FileSaver;
import ij.process.ColorProcessor;
public class CuasMultiSeries {
......@@ -156,20 +161,282 @@ public class CuasMultiSeries {
}
}
public ImagePlus generateLegend(
int annot_mode,
String title) {
final int icon_to_text = 5; // pixels
final int inter_column = 20; // pixels
final boolean scale2x = true;
final double scale = scale2x ? 2 : 1;
// final double ifov = master_CLT.getGeometryCorrection().getIFOV();
// final double radar_range = clt_parameters.imp.cuas_radar_range;
final String font_name = clt_parameters.imp.cuas_font_name;
final int font_size_radar = clt_parameters.imp.cuas_font_size; // 7; //
// final double font_ratio_radar = clt_parameters.imp.cuas_font_spacing; // 1.2; // if 0 - will use default spacing ( ~=1.5)
final int font_type = clt_parameters.imp.cuas_font_type;
final Color text_color = clt_parameters.imp.cuas_text_color;
// final Color selected_color = text_color; // clt_parameters.imp.cuas_selected_color;
// final boolean transparent_other = clt_parameters.imp.cuas_transparent;
// final boolean transparent_uas = clt_parameters.imp.cuas_transparent_uas;
final int target_type = clt_parameters.imp.cuas_target_type; // 0; // 0 - unknown, 1 - known, 2 - friend, 3 - foe
final int uas_type = clt_parameters.imp.cuas_known_type; // 2; // Target location matching UAS flight log: 0; // 0 - unknown, 1 - known, 2 - friend, 3 - foe
final double uas_radius = clt_parameters.imp.cuas_radar_uas_rad;// 4.0;
final double target_radius = clt_parameters.imp.cuas_radar_radius; // 2.5;
final Color uas_color = clt_parameters.imp.cuas_radar_uas_color; // new Color( 0,100,140);
final Color target_color = clt_parameters.imp.cuas_radar_color; // new Color( 0,255,100);
// final boolean legend_include = clt_parameters.imp.cuas_legend_include;
final Color legend_title_color = clt_parameters.imp.cuas_legend_title_color;
final int legend_title_font = clt_parameters.imp.cuas_legend_title_font;
final int legend_font = clt_parameters.imp.cuas_legend_font;
final int legend_icons = clt_parameters.imp.cuas_legend_icons;
final double legend_spacing = clt_parameters.imp.cuas_legend_spacing;
final Rectangle rect_legend = clt_parameters.imp.cuas_legend;
final Color legend_description_color = legend_title_color;
final Color legend_acronyms_color = text_color;
// final double [] camera_atr = uasLogReader.getCameraATR();
// final ErsCorrection ersCorrection = master_CLT.getErsCorrection();
boolean annotate_uas = clt_parameters.imp.cuas_annotate_uas && (uasLogReader != null); // false; // show circle around UAS position from the flight log
// prepare UAS log icon (blue circle)
ColorProcessor uaslog_cp = annotate_uas? CuasMotion.getUasLogIconColorProcessor():null;
// prepare target icons
ColorProcessor cp_target_icon = CuasMotion.getIconColorProcessor(
target_type, // int target_type, // Target location matching UAS flight log: 0; // 0 - unknown, 1 - known, 2 - friend, 3 - foe
scale2x); // boolean scale2x)
ColorProcessor cp_uas_icon = CuasMotion.getIconColorProcessor(
uas_type, // uas_type, // int target_type, // Target location matching UAS flight log: 0; // 0 - unknown, 1 - known, 2 - friend, 3 - foe
scale2x); // boolean scale2x)
final int uaslog_width = (uaslog_cp != null) ? uaslog_cp.getWidth() : 0;
final int target_icon_width = cp_target_icon.getWidth();
final int uas_icon_width = cp_uas_icon.getWidth();
final int [] uaslog_pixels = (int[]) uaslog_cp.getPixels();
final int [] target_icon_pixels = (int[]) cp_target_icon.getPixels();
final int [] uas_icon_pixels = (int[]) cp_uas_icon.getPixels();
final int [][] uaslog_rgba = CuasMotion.splitColorIcon(uaslog_pixels);
final int [][] target_icon_rgba = CuasMotion.splitColorIcon(target_icon_pixels);
final int [][] uas_icon_rgba = CuasMotion.splitColorIcon(uas_icon_pixels);
final ColorProcessor cp_legend = new ColorProcessor(rect_legend.width, rect_legend.height);
int legend_width = rect_legend.width;
int [] legend_pixels = (int[]) cp_legend.getPixels();
int font_size_description = legend_font; // font_size_radar; // separate
Font font_title = new Font(font_name, font_type, (int) Math.round (scale * legend_title_font));
Font font_description = new Font(font_name, font_type, (int) Math.round (scale * font_size_description));
Font font_acronyms = new Font(font_name, font_type, (int) Math.round (scale * font_size_description)); // font_size_radar));
int vspace_annot = (int) Math.round (Math.max(font_size_description, font_size_radar) * scale * legend_spacing);
// Icons
double icon_half_width = 0;
icon_half_width = Math.max(uas_icon_width, 0.5*uaslog_width);
icon_half_width = Math.max(uas_icon_width, 0.5*target_icon_width);
icon_half_width = Math.max(uas_icon_width, 0.5*uas_icon_width);
icon_half_width = Math.max(uas_icon_width, uas_radius);
icon_half_width = Math.max(uas_icon_width, target_radius);
// TODO - find offset from abbreviations
cp_legend.setColor(legend_description_color);
cp_legend.setFont(font_description);
int x0 = rect_legend.x, y0 = rect_legend.y;
int y0_acronyms_center = rect_legend.y + legend_icons; // align first lines
int max_x = x0;
for (int nicon = 0; nicon < CuasMotion.ICON_DESCRIPTIONS.length; nicon++) {
y0 += legend_icons;
switch (nicon) {
case 0:
CuasMotion.imprintPixelIcon(
uas_icon_rgba, // int [][] icon,
uas_icon_pixels, // int [] icon_pix,
uas_icon_width, // int icon_width,
legend_pixels, // int [] image,
legend_width, // int width,
x0, // int xc,
y0); // int yc)
break;
case 1:
CuasMotion.imprintPixelIcon(
target_icon_rgba, // int [][] icon,
target_icon_pixels, // int [] icon_pix,
target_icon_width, // int icon_width,
legend_pixels, // int [] image,
legend_width, // int width,
x0, // int xc,
y0); // int yc)
break;
case 2:
CuasMotion.imprintPixelIcon(
uaslog_rgba, // int [][] icon,
uaslog_pixels, // int [] icon_pix,
uaslog_width, // int icon_width,
legend_pixels, // int [] image,
legend_width, // int width,
x0, // int xc,
y0); // int yc)
break;
case 3:
CuasMotion.drawCircle(
cp_legend, // ColorProcessor colorProcessor,
uas_color, // Color color, // or null
x0, // double xc,
y0, // double yc,
uas_radius); // double radius)
break;
case 4:
CuasMotion.drawCircle(
cp_legend, // ColorProcessor colorProcessor,
target_color, // Color color, // or null
x0, // double xc,
y0, // double yc,
target_radius); // double radius)
break;
}
// find the height
String txt = CuasMotion.ICON_DESCRIPTIONS[nicon]; // single-line
Rectangle txt_bound = cp_legend.getStringBounds(txt);
int y_descr = y0 - (txt_bound.y + txt_bound.height/2);
int x_descr = x0+((int) icon_half_width + icon_to_text);
max_x = Math.max(max_x, x_descr + txt_bound.x + txt_bound.width);
cp_legend.setColor(legend_description_color);
cp_legend.drawString(txt, x_descr, y_descr); // transparent bg
}
// create text description
// find longest short names
int acronyms_max_end = 0;
cp_legend.setFont(font_acronyms);
cp_legend.setColor(legend_acronyms_color);
int x0_acro = max_x + inter_column;
int num_acro = 0;
int [] acro_index = new int [CuasMotion.ANNOT_LABELS.length];
int [] acro_pos_y = new int [CuasMotion.ANNOT_LABELS.length];
Arrays.fill(acro_index, -1);
for (int i = 0; i < CuasMotion.ANNOT_LABELS.length; i++) if ((annot_mode & (1 << i)) != 0){
int i_base = i;
int y0_acro = y0_acronyms_center + num_acro * vspace_annot;
String txt = CuasMotion.ANNOT_LABELS[i][0];
if ((txt == null) && ((annot_mode & (1 << (i-1))) == 0)) { // if no elevation, but conditional_elevation is enabled
i_base = i-1;
txt = CuasMotion.ANNOT_LABELS[i_base][0]; // for conditional elevation
}
if (txt != null) {
Rectangle txt_bound = cp_legend.getStringBounds(txt);
int y_acro = y0_acro - (txt_bound.y + txt_bound.height/2);
cp_legend.drawString(txt, x0_acro, y_acro); // transparent bg
acronyms_max_end = Math.max(acronyms_max_end, x0_acro + txt_bound.x + txt_bound.width);
acro_index[i_base] = num_acro++;
acro_pos_y[i_base] = y_acro;
}
}
int x0_acro_descr = acronyms_max_end + icon_to_text;
cp_legend.setFont(font_description);
cp_legend.setColor(legend_description_color);
int acronyms_descr_end = x0_acro_descr;
for (int i = 0; i < CuasMotion.ANNOT_LABELS.length; i++) if (acro_index[i] >=0) { // ((annot_mode & (1 << i)) != 0){
String txt = CuasMotion.ANNOT_LABELS[i][2];
Rectangle txt_bound = cp_legend.getStringBounds(txt);
cp_legend.drawString(txt, x0_acro_descr, acro_pos_y[i]); // transparent bg
acronyms_descr_end = Math.max(acronyms_descr_end, x0_acro_descr + txt_bound.x + txt_bound.width);
}
// print title
// int x0 = rect_legend.x
{
String txt = CuasMotion.LEGEND_TITLE;
cp_legend.setColor(legend_title_color);
cp_legend.setFont(font_title);
Rectangle txt_bound = cp_legend.getStringBounds(txt);
int offs = (acronyms_descr_end-txt_bound.width)/2;
cp_legend.drawString(txt, rect_legend.x + offs, rect_legend.y); // transparent bg
}
ImagePlus imp_legend = new ImagePlus(title, cp_legend);
// imp_legend.show();
return imp_legend;
}
public void combineVideos() {
String videoDirectory = master_CLT.correctionsParameters.selectVideoDirectory(true,true);
String extra_suffix_with_radar = "-2";
// double video_fps = clt_parameters.imp.video_fps;
String video_codec_combo = clt_parameters.imp.video_codec.toLowerCase();
int video_crf_combo = clt_parameters.imp.video_crf;
double video_bitrate_m = clt_parameters.imp.video_bitrate_m;
double video_bitrate_m = clt_parameters.imp.video_bitrate_m;
int corr_pairs = clt_parameters.imp.cuas_corr_pairs;
boolean ra_background = clt_parameters.imp.cuas_ra_background; // true;
String ra_bg_suffix=(ra_background? ("-RABG"+corr_pairs):"");
String clean_suffix = "-CLEAN"; // CuasMotion.CLEAN_SUFFIX
boolean legend_include = clt_parameters.imp.cuas_legend_include;
double legend_duration = clt_parameters.imp.cuas_legend_duration;
if (videoDirectory == null) {
System.out.println("No video directory selected");
return;
}
File video_dir = new File (videoDirectory);
video_dir.mkdirs(); // Should already exist
int full_annot_mode = clt_parameters.imp.cuas_annot_sel[CuasMotion.ANNOT_RADAR_PANE_INDX] | // both panes
clt_parameters.imp.cuas_annot_sel[CuasMotion.ANNOT_IMAGE_PANE_INDX];
int clean_annot_mode = full_annot_mode & clt_parameters.imp.cuas_annot_sel[CuasMotion.ANNOT_CLEAN_VIEW_INDX];
ImagePlus [] imp_legends = new ImagePlus[2];
String [] legend_paths = new String[2];
for (int iclean = 0; iclean <2; iclean++) {
boolean clean = iclean > 0;
int annot_mode = clean ? clean_annot_mode : full_annot_mode;
// use first series name
String title = model_names[0]+"-LEGEND"+(clean?CuasMotion.CLEAN_SUFFIX: "")+".tiff";
imp_legends[iclean] = generateLegend(
annot_mode, // int annot_mode,
title); // String title));
legend_paths[iclean] = QuadCLT.saveImagePlusInDirectory(
imp_legends[iclean], // ImagePlus imp,
video_dir.toString()); // String dir)
}
File legend_vfile = null;
if (legend_include) { // only include clean legend
double pts_scale = clt_parameters.imp.video_fps/clt_parameters.imp.sensor_fps;
pts_scale = 1.0;
String legend_path = legend_paths[1];
String legend_vpath = legend_path;
int ind_dot = legend_vpath.lastIndexOf(".");
legend_vpath= legend_vpath.substring(0,ind_dot)+".webm";
legend_vfile = new File(legend_vpath);
String shell_legend_include = String.format("ffmpeg -y -loop 1 -i %s -r 60 -vf setpts=%f*PTS -b:v %fM -crf %d -c %s -t %f -pix_fmt yuv420p %s",
legend_paths[1],
pts_scale,
video_bitrate_m,
video_crf_combo,
video_codec_combo,
legend_duration,
legend_vpath);
boolean legend_ok = runShellCommand(
shell_legend_include, // String shellCommand,
video_dir, // File work_dir,
legend_vfile);
if (!legend_ok) {
legend_vfile = null;
}
}
System.out.println();
ArrayList<String> video_paths = new ArrayList<String>();
if ((legend_vfile != null)&& legend_vfile.exists()) {
video_paths.add(legend_vfile.toString());
}
// int annot_mode = -1; // specify bits
int corr_pairs = clt_parameters.imp.cuas_corr_pairs;
boolean ra_background = clt_parameters.imp.cuas_ra_background; // true;
String ra_bg_suffix=(ra_background? ("-RABG"+corr_pairs):"");
String clean_suffix = "-CLEAN";
for (int nser = 0; nser < model_names.length; nser++) {
String image_name = model_names[nser];
String webm_title = image_name+CuasMotion.getParametersSuffixRslt(clt_parameters,"-RGB"+ra_bg_suffix+clean_suffix)+extra_suffix_with_radar+".webm";
......@@ -180,13 +447,6 @@ public class CuasMultiSeries {
}
}
System.out.println("Combining "+video_paths.size()+" video files.");
String videoDirectory = master_CLT.correctionsParameters.selectVideoDirectory(true,true);
if (videoDirectory == null) {
System.out.println("No video directory selected");
return;
}
File video_dir = new File (videoDirectory);
video_dir.mkdirs(); // Should already exist
String combo_video_name = "COMBO"+CuasMotion.getParametersSuffixRslt(clt_parameters,"-RGB"+ra_bg_suffix+clean_suffix)+extra_suffix_with_radar+".webm";
String concat_list_name = combo_video_name.substring(0, combo_video_name.lastIndexOf("."))+".list";
......@@ -234,6 +494,12 @@ public class CuasMultiSeries {
shellCommand = String.format("ffmpeg -y -f concat -safe 0 -i %s -r 60 -vf setpts=%f*PTS -b:v %fM -crf %d -c %s %s",
list_to_concat.toString(), pts_scale, video_bitrate_m, video_crf_combo, video_codec_combo, video_out.toString());
boolean shell_ok = runShellCommand(
shellCommand, // String shellCommand,
video_dir, // File work_dir,
video_out); // File out_file) { // if not null will delete this file and verify its creation
/*
Process p = null;
int exit_code = -1;
System.out.println("Will run shell command: \""+shellCommand+"\"");
......@@ -262,10 +528,60 @@ public class CuasMultiSeries {
if ((exit_code != 0) || !video_out.exists()) {
System.out.println("Failed to create : \""+video_out.toString()+"\"");
}
*/
System.out.println("All "+(shell_ok? "done":"failed"));
return;
}
public boolean runShellCommand(
String shellCommand,
File work_dir,
File out_file) { // if not null will delete this file and verify its creation
if ((out_file != null) && out_file.exists()) {
out_file.delete();
}
Process p = null;
int exit_code = -1;
System.out.println("Will run shell command: \""+shellCommand+"\"");
System.out.println("This may take a while, please wait ...");
try {
p = Runtime.getRuntime().exec(
shellCommand,
null, // env
work_dir // working dir - needed if "-report" is added to ffmpeg command
);
} catch (IOException e) {
System.out.println("Failed shell command: \""+shellCommand+"\"");
}
if (p != null) {
try {
p.waitFor();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
exit_code = p.exitValue();
}
System.out.println("Ran shell command: \""+shellCommand+"\" -> ");
if ((exit_code != 0) || ((out_file != null) && !out_file.exists())) {
System.out.println("Failed to create output in : \""+work_dir.toString()+"\"");
return false;
} else {
//out_file
if (out_file != null) {
System.out.println("Created : \""+out_file.toString()+" in \""+work_dir.toString()+"\"");
} else {
System.out.println("Finished w/o errors in \""+work_dir.toString()+"\"");
}
return true;
}
}
public void processGlobals() {
int debugLevel = 0;
int setup_uas = setupUasTiles();
......
......@@ -2762,18 +2762,21 @@ public class CuasRanging {
sb.append("\n"); // there will be 1 extra blank column
String [] slice_titles = cuasMotion.getSliceTitles(); // timestamps
UasLogReader uasLogReader = cuasMotion.getUasLogReader();
ErsCorrection ersCorrection = center_CLT.getErsCorrection();
for (int nseq = 0; nseq < num_seq; nseq++) {
String timestamp = slice_titles[nseq];
sb.append(nseq+"\t"+timestamp+"\t");
// get azimuth, elevation, target disparity from the log plus infinity, log range
double [] uas_pXpYDRange = uasLogReader.getUasPxPyDRange(timestamp); // px, py, d- cuas_infinity (true disparity), range
double [][] az_el_oaz_oel= CuasMotion.getPixToAzElev(
clt_parameters, // CLTParameters clt_parameters,
gc, // GeometryCorrection gc,
ersCorrection, // ErsCorrection ersCorrection,
uasLogReader, // UasLogReader uasLogReader,
Double.NaN, // double fps, // if NaN, will use default 60Hz. Used only for omegas
uas_pXpYDRange[0], // double target_x,
uas_pXpYDRange[1], // double target_y,
0, // double target_vx,
0); // double target_vy);
sb.append(uas_pXpYDRange[0]+"\t"+uas_pXpYDRange[1]+"\t"+az_el_oaz_oel[0][0]+"\t"+az_el_oaz_oel[0][1]+"\t"+(uas_pXpYDRange[2]+cuas_infinity)+"\t"+uas_pXpYDRange[3]+"\t");
for (int ntarg = 0; ntarg < num_targets; ntarg++) {
......@@ -2794,15 +2797,16 @@ public class CuasRanging {
double yc = tileSize * tileY + tileSize/2 + target[CuasMotionLMA.RSLT_Y];
double vx = target[CuasMotionLMA.RSLT_VX];
double vy = target[CuasMotionLMA.RSLT_VY];
// calculate and output target azimuth, elevation, disparity (full) and range
az_el_oaz_oel= CuasMotion.getPixToAzElev(
clt_parameters, // CLTParameters clt_parameters,
gc, // GeometryCorrection gc,
xc, // double target_x, // null
yc, // double target_y,
vx, // double target_vx,
vy); // double target_vy);
ersCorrection, // ErsCorrection ersCorrection,
uasLogReader, // UasLogReader uasLogReader,
Double.NaN, // double fps, // if NaN, will use default 60Hz. Used only for omegas
xc, // double px, // null
yc, // double py,
vx, // double vx,
vy); // double vy);
sb.append(xc+"\t"+yc+"\t"+az_el_oaz_oel[0][0]+"\t"+az_el_oaz_oel[0][1]+"\t"+
target[CuasMotionLMA.RSLT_DISPARITY]+"\t"+target[CuasMotionLMA.RSLT_RANGE]+"\t");
if (target[CuasMotionLMA.RSLT_GLENGTH] > 0) {
......
......@@ -49,6 +49,7 @@ public class IntersceneMatchParameters {
public static Color DEFAULT_cuas_radar_uas_color =new Color( 0,100,140);
public static Color DEFAULT_cuas_radar_color = new Color( 0,255,100);
public static Color DEFAULT_cuas_grid_color = new Color(100,100,100);
public static Color DEFAULT_cuas_legend_color =new Color( 255,255,255); // legend title color
......@@ -869,18 +870,18 @@ min_str_neib_fpn 0.35
public double cuas_font_spacing = 1.2; //height to size ratio. If 0 - will use default spacing ( ~=1.5)
public boolean cuas_annot_missing = false; // Reserve a line for requested but missing parameters
// AZ/EL calibration
@Deprecated
public double cuas_ifov = 0.05; // degree per pixel Use gc.getIFOVDegrees() and gc.getIFOV() instead
public int cuas_px0 = 283; // pixel with known azimuth
public int cuas_py0 = 386; // pixel with known elevation
public double cuas_az0 = 201.5; // degrees for cuas_px0;
public double cuas_el0 = 0.0; // degrees for cuas_px0;
public boolean cuas_show_disp = true; // Show disparity (corrected) near target (not in clean)
public boolean cuas_show_rng = true; // Show distance to target (range) in meters
public boolean cuas_legend_generate = true; //
public boolean cuas_legend_include = true; // include legend in the video
public double cuas_legend_duration = 5.0; // seconds in the beginning of the video
public Color cuas_legend_title_color = DEFAULT_cuas_legend_color;
public int cuas_legend_title_font = 18;
public int cuas_legend_font = 10; // before scaling 2x (to match other sizes)
public int cuas_legend_icons = 60; // icons vertical spacing, pixels
public double cuas_legend_spacing = 1.5; // height to size ratio. If 0 - will use default spacing ( ~=1.5)
public Rectangle cuas_legend = new Rectangle(50, 150, 1820, 1024); // Legend location and image size (same as video)
public boolean cuas_show_inf = true; // Show distance greater than max (or negativce) as infinity
public boolean cuas_show_inf_gt = true; // Use ">max" instead of infinity symbol
public boolean cuas_show_true_rng = true; // show true range (from the flight log)
// ranging parameters
public boolean cuas_smooth_omegas = true; // Recalculate omegas from continuing targets positions
......@@ -954,7 +955,7 @@ min_str_neib_fpn 0.35
public Color cuas_radar_uas_color = DEFAULT_cuas_radar_uas_color; // new Color( 0,100,140); True UAS position sircle
public Color cuas_radar_color = DEFAULT_cuas_radar_color; //new Color( 0,255,100); Detected color
public double cuas_radar_radius = 2.5;
public double cuas_radar_uas_rad = 4.0;
public double cuas_radar_uas_rad = 7.0; // 4.0;
// parameters for "radar" grid generation
public double cuas_grid_ring = 100.0; // (m) range rings step in meters
public double cuas_grid_line = 5.0; // (deg) grid axial directions step
......@@ -2680,26 +2681,33 @@ min_str_neib_fpn 0.35
"Height to size ratio. If 0 - will use default spacing ( ~=1.5).");
gd.addCheckbox ("Reserve lines for undefined parameters", this.cuas_annot_missing,
"Reserve line for requested but missing parameters.");
gd.addNumericField("Known image pixel X coordinate", this.cuas_px0, 0,3,"",
"Image pixel X corresponding to the known azimuth.");
gd.addNumericField("Known image pixel X coordinate", this.cuas_py0, 0,3,"",
"Image pixel Y corresponding to the known elevation.");
gd.addNumericField("Known pixel azimuth", this.cuas_az0, 5,8,"degree",
"Azimuth corresponding to the known pixel X.");
gd.addNumericField("Known pixel elevation", this.cuas_el0, 5,8,"degree",
"Elevation corresponding to the known pixel Y.");
gd.addCheckbox ("Show target disparity", this.cuas_show_disp,
"Show disparity before infinity correction (not in clean mode).");
gd.addCheckbox ("Show target distance (meters)", this.cuas_show_rng,
"Show range true from the UAS log.");
gd.addMessage("=== Legend generation === ");
gd.addCheckbox ("Generate legend", this.cuas_legend_generate,
"Generate legend in the same directory as composite video when genarating composite video.");
gd.addCheckbox ("Include legend in video", this.cuas_legend_include,
"Include generated legend in the beginning of the composite video.");
gd.addNumericField("Legend duration in video", this.cuas_legend_duration, 5,8,"s",
"Legend duration, in seconds");
gd.addStringField ("Legend title color", getStringColor(this.cuas_legend_title_color, DEFAULT_cuas_legend_color), 8,
"legend font color. Any wrong hex value will be replaced by the default - "+getStringColor(DEFAULT_cuas_legend_color));
gd.addNumericField("Legend title font size", this.cuas_legend_title_font, 0,3,"",
"Actual font size will be double that as we are using fixed scale = 2.0 for all images.");
gd.addNumericField("Legend font size", this.cuas_legend_font, 0,3,"",
"Actual font size will be double that as we are using fixed scale = 2.0 for all images.");
gd.addNumericField("Legend icons spacing", this.cuas_legend_icons, 0,3,"pix",
"Vertical spacing between legend icons");
gd.addNumericField("Legend font spacing", this.cuas_legend_spacing, 5,8,"x",
"Height to size ratio. If 0 - will use default spacing ( ~=1.5).");
gd.addStringField ("Legend offset and image size", rectangleToString(cuas_legend), 40, "Legend title offset from the top-left corner and image sized.");
gd.addMessage("=== Ranging parameters ===");
gd.addCheckbox ("Show infinity distance to target", this.cuas_show_inf,
"Show when distance is above limit (unchecked - skip the line).");
gd.addCheckbox ("Show infinity as >range_limit", this.cuas_show_inf_gt,
"Show infinity as >range_limit, unchecked - as infinity symbol.");
gd.addCheckbox ("Show true range", this.cuas_show_true_rng,
"Show range true from the UAS log.");
gd.addMessage("=== Ranging parameters ===");
gd.addCheckbox ("Smooth omegas", this.cuas_smooth_omegas,
"Recalculate omegas from continuing targets positions.");
gd.addCheckbox ("Apply unsharp mask to images for ranging", this.cuas_rng_um,
......@@ -2852,12 +2860,12 @@ min_str_neib_fpn 0.35
gd.addMessage(CuasMotion.ANNOT_TIP);
for (int i = 0; i < CuasMotion.ANNOT_LABELS.length; i++) {
gd.addStringField(
CuasMotion.ANNOT_LABELS[i][0], // String label,
CuasMotion.ANNOT_LABELS[i][1], // String label,
CuasMotion.getCuasAnnot ( //String value,
i, // int indx,
cuas_annot_sel), // int [] annots),
8, // int width,
CuasMotion.ANNOT_LABELS[i][1]); // String tooltip)
CuasMotion.ANNOT_LABELS[i][2]); // String tooltip)
}
gd.addMessage("=== Debug ===");
......@@ -4013,15 +4021,21 @@ min_str_neib_fpn 0.35
this.cuas_font_type = (int) gd.getNextNumber();
this.cuas_font_spacing = gd.getNextNumber();
this.cuas_annot_missing = gd.getNextBoolean();
this.cuas_px0 = (int) gd.getNextNumber();
this.cuas_py0 = (int) gd.getNextNumber();
this.cuas_az0 = gd.getNextNumber();
this.cuas_el0 = gd.getNextNumber();
this.cuas_show_disp = gd.getNextBoolean();
this.cuas_show_rng = gd.getNextBoolean();
this.cuas_legend_generate = gd.getNextBoolean();
this.cuas_legend_include = gd.getNextBoolean();
this.cuas_legend_duration = gd.getNextNumber();
this.cuas_legend_title_color = getColorFromHex(
gd.getNextString(), //String hex_color,
DEFAULT_cuas_legend_color); // Color default_color)
this.cuas_legend_title_font = (int) gd.getNextNumber();
this.cuas_legend_font = (int) gd.getNextNumber();
this.cuas_legend_icons = (int) gd.getNextNumber();
this.cuas_legend_spacing = gd.getNextNumber();
this.cuas_legend = stringToRectangle(gd.getNextString());// Rectangle
this.cuas_show_inf = gd.getNextBoolean();
this.cuas_show_inf_gt = gd.getNextBoolean();
this.cuas_show_true_rng = gd.getNextBoolean();
this.cuas_smooth_omegas = gd.getNextBoolean();
this.cuas_rng_um = gd.getNextBoolean();
......@@ -5154,15 +5168,19 @@ min_str_neib_fpn 0.35
properties.setProperty(prefix+"cuas_font_type", this.cuas_font_type+""); // int
properties.setProperty(prefix+"cuas_font_spacing", this.cuas_font_spacing+""); // double
properties.setProperty(prefix+"cuas_annot_missing", this.cuas_annot_missing+""); // boolean
properties.setProperty(prefix+"cuas_px0", this.cuas_px0+""); // int
properties.setProperty(prefix+"cuas_py0", this.cuas_py0+""); // int
properties.setProperty(prefix+"cuas_az0", this.cuas_az0+""); // double
properties.setProperty(prefix+"cuas_el0", this.cuas_el0+""); // double
properties.setProperty(prefix+"cuas_show_disp", this.cuas_show_disp+""); // boolean
properties.setProperty(prefix+"cuas_show_rng", this.cuas_show_rng+""); // boolean
properties.setProperty(prefix+"cuas_legend_generate", this.cuas_legend_generate+""); // boolean
properties.setProperty(prefix+"cuas_legend_include", this.cuas_legend_include+""); // boolean
properties.setProperty(prefix+"cuas_legend_duration", this.cuas_legend_duration+""); // double
properties.setProperty(prefix+"cuas_legend_title_color", getStringColor(this.cuas_legend_title_color, DEFAULT_cuas_legend_color)); // Color
properties.setProperty(prefix+"cuas_legend_title_font", this.cuas_legend_title_font+"");// int
properties.setProperty(prefix+"cuas_cuas_legend_font", this.cuas_legend_font+""); // int
properties.setProperty(prefix+"cuas_legend_icons", this.cuas_legend_icons+""); // int
properties.setProperty(prefix+"cuas_legend_spacing", this.cuas_legend_spacing+""); // double
properties.setProperty(prefix+"cuas_legend", rectangleToString(cuas_legend)+""); // Rectangle
properties.setProperty(prefix+"cuas_show_inf", this.cuas_show_inf+""); // boolean
properties.setProperty(prefix+"cuas_show_inf_gt", this.cuas_show_inf_gt+""); // boolean
properties.setProperty(prefix+"cuas_show_true_rng", this.cuas_show_true_rng+""); // boolean
properties.setProperty(prefix+"cuas_smooth_omegas", this.cuas_smooth_omegas+""); // boolean
properties.setProperty(prefix+"cuas_rng_um", this.cuas_rng_um+""); // boolean
......@@ -6257,15 +6275,22 @@ min_str_neib_fpn 0.35
if (properties.getProperty(prefix+"cuas_font_type")!=null) this.cuas_font_type=Integer.parseInt(properties.getProperty(prefix+"cuas_font_type"));
if (properties.getProperty(prefix+"cuas_font_spacing")!=null) this.cuas_font_spacing=Double.parseDouble(properties.getProperty(prefix+"cuas_font_spacing"));
if (properties.getProperty(prefix+"cuas_annot_missing")!=null) this.cuas_annot_missing=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_annot_missing"));
if (properties.getProperty(prefix+"cuas_px0")!=null) this.cuas_px0=Integer.parseInt(properties.getProperty(prefix+"cuas_px0"));
if (properties.getProperty(prefix+"cuas_py0")!=null) this.cuas_py0=Integer.parseInt(properties.getProperty(prefix+"cuas_py0"));
if (properties.getProperty(prefix+"cuas_az0")!=null) this.cuas_az0=Double.parseDouble(properties.getProperty(prefix+"cuas_az0"));
if (properties.getProperty(prefix+"cuas_el0")!=null) this.cuas_el0=Double.parseDouble(properties.getProperty(prefix+"cuas_el0"));
if (properties.getProperty(prefix+"cuas_show_disp")!=null) this.cuas_show_disp=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_show_disp"));
if (properties.getProperty(prefix+"cuas_show_rng")!=null) this.cuas_show_rng=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_show_rng"));
if (properties.getProperty(prefix+"cuas_legend_generate")!=null) this.cuas_legend_generate=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_legend_generate"));
if (properties.getProperty(prefix+"cuas_legend_include")!=null) this.cuas_legend_include=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_legend_include"));
if (properties.getProperty(prefix+"cuas_legend_duration")!=null) this.cuas_legend_duration=Double.parseDouble(properties.getProperty(prefix+"cuas_legend_duration"));
if (properties.getProperty(prefix+"cuas_legend_title_color")!=null) this.cuas_radar_uas_color = getColorFromHex(
properties.getProperty(prefix+"cuas_legend_title_color"), // String hex_color,
DEFAULT_cuas_legend_color); // Color default_color)
if (properties.getProperty(prefix+"cuas_legend_title_font")!=null) this.cuas_legend_title_font=Integer.parseInt(properties.getProperty(prefix+"cuas_legend_title_font"));
if (properties.getProperty(prefix+"cuas_legend_font")!=null) this.cuas_legend_font=Integer.parseInt(properties.getProperty(prefix+"cuas_legend_font"));
if (properties.getProperty(prefix+"cuas_legend_icons")!=null) this.cuas_legend_icons=Integer.parseInt(properties.getProperty(prefix+"cuas_legend_icons"));
if (properties.getProperty(prefix+"cuas_legend_spacing")!=null) this.cuas_legend_spacing=Double.parseDouble(properties.getProperty(prefix+"cuas_legend_spacing"));
if (properties.getProperty(prefix+"cuas_legend")!=null) this.cuas_legend=stringToRectangle((String) properties.getProperty(prefix+"cuas_legend"));
if (properties.getProperty(prefix+"cuas_show_inf")!=null) this.cuas_show_inf=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_show_inf"));
if (properties.getProperty(prefix+"cuas_show_inf_gt")!=null) this.cuas_show_inf_gt=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_show_inf_gt"));
if (properties.getProperty(prefix+"cuas_show_true_rng")!=null) this.cuas_show_true_rng=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_show_true_rng"));
if (properties.getProperty(prefix+"cuas_smooth_omegas")!=null) this.cuas_smooth_omegas=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_smooth_omegas"));
if (properties.getProperty(prefix+"cuas_rng_um")!=null) this.cuas_rng_um=Boolean.parseBoolean(properties.getProperty(prefix+"cuas_rng_um"));
......@@ -7355,15 +7380,19 @@ min_str_neib_fpn 0.35
imp.cuas_font_type = this.cuas_font_type;
imp.cuas_font_spacing = this.cuas_font_spacing;
imp.cuas_annot_missing = this.cuas_annot_missing;
imp.cuas_px0 = this.cuas_px0;
imp.cuas_py0 = this.cuas_py0;
imp.cuas_az0 = this.cuas_az0;
imp.cuas_el0 = this.cuas_el0;
imp.cuas_show_disp = this.cuas_show_disp;
imp.cuas_show_rng = this.cuas_show_rng;
imp.cuas_legend_generate = this.cuas_legend_generate;
imp.cuas_legend_include = this.cuas_legend_include;
imp.cuas_legend_duration = this.cuas_legend_duration;
imp.cuas_legend_title_color = this.cuas_legend_title_color;
imp.cuas_legend_title_font = this.cuas_legend_title_font;
imp.cuas_legend_font = this.cuas_legend_font;
imp.cuas_legend_icons = this.cuas_legend_icons;
imp.cuas_legend_spacing = this.cuas_legend_spacing;
imp.cuas_legend = new Rectangle(this.cuas_legend);
imp.cuas_show_inf = this.cuas_show_inf;
imp.cuas_show_inf_gt = this.cuas_show_inf_gt;
imp.cuas_show_true_rng = this.cuas_show_true_rng;
imp.cuas_smooth_omegas = this.cuas_smooth_omegas;
imp.cuas_rng_um = this.cuas_rng_um;
......
......@@ -6063,7 +6063,28 @@ public class QuadCLTCPU {
System.out.println("saveDoubleArrayInModelDirectory(): saved "+file_path);
return file_path;
}
public static String saveImagePlusInDirectory(
ImagePlus imp,
String dir) {
String file_name = imp.getTitle();
if (!file_name.endsWith(".tiff")) {
file_name +=".tiff";
}
if (!dir.endsWith(Prefs.getFileSeparator())) {
dir += Prefs.getFileSeparator();
}
String file_path = dir + file_name; // + ".tiff";
FileSaver fs=new FileSaver(imp);
fs.saveAsTiff(file_path); // image processor null?
System.out.println("saveImagePlusInDirectory(): saved "+file_path);
return file_path;
}
/*
File list_to_concat = new File (video_dir,concat_list_name);
*/
public String saveConfInModelDirectory()
{
String x3d_path = getX3dDirectory();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment