Commit b3dc65da authored by Andrey Filippov's avatar Andrey Filippov

refactoring, resting sfm low-level

parent d693f718
......@@ -128,7 +128,9 @@ public class GpuQuad{ // quad camera description
private int [] sensor_mask_inter_indices = null;
private int [] sensor_mask_inter_indices_neibs = null;
public int getNumCorrTiles() {
return num_corr_tiles;
}
public boolean [] getCorrMask() {
boolean [] corr_mask_boolean = new boolean [num_all_pairs];
for (int i = 0; i < num_all_pairs; i++) {
......@@ -3000,7 +3002,7 @@ public class GpuQuad{ // quad camera description
final int corr_size_td = 4 * GPUTileProcessor.DTT_SIZE * GPUTileProcessor.DTT_SIZE;
int [] indices = getCorrIndices(); // also sets num_corr_tiles
float [] fdata = getCorrTdData();
int num_tiles = num_corr_tiles / num_pairs;
final int num_tiles = num_corr_tiles / num_pairs;
int width = corr_tiles[0].length;
for (int nt = 0; nt < num_tiles; nt++ ) {
int nTile = (indices[nt * num_pairs] >> GPUTileProcessor.CORR_NTILE_SHIFT);
......@@ -3009,11 +3011,46 @@ public class GpuQuad{ // quad camera description
corr_tiles[ty][tx] = new float [num_pairs][corr_size_td];
for (int pair = 0; pair < num_pairs; pair++) {
System.arraycopy(fdata, (nt * num_pairs + pair) * corr_size_td, corr_tiles[ty][tx][pair], 0, corr_size_td);
System.arraycopy(
fdata,
(nt * num_pairs + pair) * corr_size_td,
corr_tiles[ty][tx][pair],
0,
corr_size_td);
}
}
return corr_tiles;
}
public float [][] getCorrTilesTdInterCombo(
final float [][] corr_tiles_combo){ // initialized to new float [numer_of_image_tiles][]
int num_pairs = getNumCamsInter() + 1;
int index_combo = num_pairs - 1;
final int corr_size_td = 4 * GPUTileProcessor.DTT_SIZE * GPUTileProcessor.DTT_SIZE;
final int [] indices = getCorrIndices(); // also sets num_corr_tiles
final float [] fdata = getCorrTdData();
int num_tiles = num_corr_tiles / num_pairs;
final Thread[] threads = ImageDtt.newThreadArray(ImageDtt.THREADS_MAX);
final AtomicInteger ai = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int nt = ai.getAndIncrement(); nt < num_tiles; nt = ai.getAndIncrement()) {
int nTile = (indices[nt * num_pairs] >> GPUTileProcessor.CORR_NTILE_SHIFT);
corr_tiles_combo[nTile] = new float [corr_size_td];
System.arraycopy(
fdata,
(nt * num_pairs + index_combo) * corr_size_td,
corr_tiles_combo[nTile],
0,
corr_size_td);
}
}
};
}
ImageDtt.startAndJoin(threads);
return corr_tiles_combo;
}
/**
* Read GPU correlation data to the sparse array [tilesY][tilesX][] for a single correlation pair (usually a combo one)
......
......@@ -991,6 +991,7 @@ public class ImageDtt extends ImageDttCPU {
* @param imgdtt_params
* @param tp_tasks (tasks should not include the tiles that are missing from the reference scene)
* @param fcorr_td null or float [tilesY][tilesX][][] - will return [number_of_selected_sensors + 1][256] for non-empty
* @param fcorr_combo_td null or float [tilesY*tilesX][] - will return [256] for non-empty
* @param gpu_sigma_r
* @param gpu_sigma_b
* @param gpu_sigma_g
......@@ -1103,6 +1104,7 @@ public class ImageDtt extends ImageDttCPU {
* @param tp_tasks (tasks should not include the tiles that are missing from the reference scene)
* Differently from nonMB interCorrTD(), tasks contain a pair of primary (to set) and secondary (to subtract)
* @param fcorr_td null or float [tilesY][tilesX][][] - will return [number_of_selected_sensors + 1][256] for non-empty
* @param fcorr_combo_td null or float [tilesY*tilesX][] - will return [256] for non-empty
* @param gpu_sigma_r
* @param gpu_sigma_b
* @param gpu_sigma_g
......@@ -1116,8 +1118,8 @@ public class ImageDtt extends ImageDttCPU {
* @param threadsMax
* @param globalDebugLevel
*/
public void interCorrTDMotionBlur(
// GpuQuad gpuQuad,
final ImageDttParameters imgdtt_params, // Now just extra correlation parameters, later will include, most others
final TpTask[][] tp_tasks,
final float [][][][] fcorr_td, // [tilesY][tilesX][pair][4*64] transform domain representation of 6 corr pairs
......@@ -1925,21 +1927,6 @@ public class ImageDtt extends ImageDttCPU {
if (mv_pd != null) for (int i = 0; i < mv_pd.length; i++) mv[i] += w_pd * mv_pd[i];
if (mv_td != null) for (int i = 0; i < mv_td.length; i++) mv[i] += w_td * mv_td[i];
if (mv_neib != null) for (int i = 0; i < mv_neib.length; i++) mv[i] += w_ntd * mv_neib[i];
/*
/// mv = mv_pd;
System.arraycopy(mv_pd, 0, mv, 0, 3); // keep [3] for optional disparities
// mv[2] *= pd_weight;
if ((mv_td != null) && !td_nopd_only) { // mix
mv[0] = (mv[0] * pd_weight + mv_td[0] * td_weight)/ (pd_weight + td_weight);
mv[1] = (mv[1] * pd_weight + mv_td[1] * td_weight)/ (pd_weight + td_weight);
mv[2] = (mv[2] * pd_weight + mv_td[2] * td_weight)/ (pd_weight + td_weight);
} // mix
} else { // (mv_pd == null) && (mv_td != null) below
/// mv = mv_td;
System.arraycopy(mv_td, 0, mv, 0, 3); // keep [3] for optional disparities
//mv[2] *= td_weight;
}
*/
if (mv != null) {
if (pXpYD == null) {
coord_motion[0][nTile] = mv;
......
......@@ -44,7 +44,7 @@ import Jama.Matrix;
import ij.ImageStack;
public class ImageDttCPU {
static final int THREADS_MAX = 100;
public static final int THREADS_MAX = 100;
static boolean FPGA_COMPARE_DATA= false; // true; // false; //
static int FPGA_SHIFT_BITS = 7; // number of bits for fractional pixel shift
static int FPGA_PIXEL_BITS = 15; // bits to represent pixel data (positive)
......@@ -8750,7 +8750,6 @@ public class ImageDttCPU {
final int tilesY=fcorr_data.length/tilesX;
final int nTiles=tilesX*tilesY;
final int tile_size = corr_size+1;
// final int corr_len = corr_size*corr_size;
final float [][] fcorr_data_out = new float[layers][tilesY*tilesX*tile_size*tile_size];
final Thread[] threads = newThreadArray(threadsMax);
final AtomicInteger ai = new AtomicInteger(0);
......@@ -8785,6 +8784,54 @@ public class ImageDttCPU {
return fcorr_data_out;
}
public static double [][] corr_partial_dbg( // not used in lwir
final double [][][] corr_data, // [layer][tile][(2*transform_size-1)*(2*transform_size-1)] // if null - will not calculate
final int tilesX,
final int corr_size, // 15
final double border_contrast,
final int globalDebugLevel)
{
final int layers = corr_data.length;
final int tilesY=corr_data[0].length/tilesX;
final int nTiles=tilesX*tilesY;
final int tile_size = corr_size+1;
final double [][] corr_data_out = new double[layers][tilesY*tilesX*tile_size*tile_size];
final Thread[] threads = newThreadArray(THREADS_MAX);
final AtomicInteger ai = new AtomicInteger(0);
for (int layer = 0; layer < layers; layer++) {
Arrays.fill(corr_data_out[layer], Double.NaN);
}
for (int layer = 0; layer < layers; layer++) {
ai.set(0);
final int flayer = layer;
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
@Override
public void run() {
int tileY,tileX;
for (int nTile = ai.getAndIncrement(); nTile < nTiles; nTile = ai.getAndIncrement()) {
tileY = nTile/tilesX;
tileX = nTile - tileY * tilesX;
if (corr_data[flayer][nTile] != null) {
for (int i = 0; i < corr_size;i++){
System.arraycopy(
corr_data[flayer][nTile],
corr_size* i,
corr_data_out[flayer],
((tileY*tile_size + i) *tilesX + tileX)*tile_size ,
corr_size);
}
}
}
}
};
}
startAndJoin(threads);
}
return corr_data_out;
}
// 2021 version, uses tp_tasks as an index to fcorr_data
public static float [][] corr_partial_dbg( // not used in lwir
final float [][][] fcorr_data, // [tile][pair][(2*transform_size-1)*(2*transform_size-1)] // if null - will not calculate
......
......@@ -174,8 +174,7 @@ public class IntersceneLma {
String fmt = String.format("[%%%d.%df, %%%d.%df, %%%d.%df]", w,d,w,d,w,d);
return String.format(fmt, vector[0], vector[1], vector[2]);
}
public void prepareLMA(
final double [] scene_xyz0, // camera center in world coordinates (or null to use instance)
final double [] scene_atr0, // camera orientation relative to world frame (or null to use instance)
......
......@@ -42,6 +42,10 @@ public class IntersceneMatchParameters {
public double [] ims_mount_atr = {0, 0, 0}; // IMS mount fine correction (A,T,R around camera axes)
public double [] ims_mount_xyz = {0, 0, 0}; // IMS center in camera coordinates
public boolean sfm_use = true; // use SfM to improve depth map
public double sfm_min_base = 2.0; // use SfM if baseline exceeds this
public double [] getImsMountATR() {
return new double [] {
ims_mount_atr[0] * Math.PI/180,
......@@ -430,6 +434,12 @@ public class IntersceneMatchParameters {
"IMS mount fine correction (A,T,R around camera axes).");
gd.addStringField ("IMS mount XYZ correction (m)", IntersceneMatchParameters.doublesToString(ims_mount_xyz), 80,
"MS center (X,Y,Z m) in camera coordinates.");
gd.addTab ("SfM", "Structure from Motion to improve depth map for the lateral views");
gd.addCheckbox ("Use SfM", this.sfm_use,
"Use SfM for the depth map enhancement for laterally moving camera.");
gd.addNumericField("Minimal SfM baseline", this.sfm_min_base, 5,8,"m",
"Use SfM only if the baseline (lateral offset between scenes in a series) exceeds this value.");
gd.addTab ("Scene Series", "Processing series of scenes and multi-series sets");
gd.addMessage ("Build series options");
......@@ -1096,6 +1106,8 @@ public class IntersceneMatchParameters {
this.ims_ortho = IntersceneMatchParameters. StringToDoubles(gd.getNextString(), 4);
this.ims_mount_atr = IntersceneMatchParameters. StringToDoubles(gd.getNextString(), 3);
this.ims_mount_xyz = IntersceneMatchParameters. StringToDoubles(gd.getNextString(), 3);
this.sfm_use = gd.getNextBoolean();
this.sfm_min_base = gd.getNextNumber();
this.force_ref_dsi = gd.getNextBoolean();
this.force_orientations = gd.getNextBoolean();
this.run_ly = gd.getNextBoolean();
......@@ -1446,7 +1458,9 @@ public class IntersceneMatchParameters {
properties.setProperty(prefix+"gmt_plus", this.gmt_plus+""); // double
properties.setProperty(prefix+"ims_ortho", IntersceneMatchParameters.doublesToString(this.ims_ortho));
properties.setProperty(prefix+"ims_mount_atr", IntersceneMatchParameters.doublesToString(this.ims_mount_atr));
properties.setProperty(prefix+"ims_mount_xyz", IntersceneMatchParameters.doublesToString(this.ims_mount_xyz));
properties.setProperty(prefix+"ims_mount_xyz", IntersceneMatchParameters.doublesToString(this.ims_mount_xyz));
properties.setProperty(prefix+"sfm_use", this.sfm_use + ""); // boolean
properties.setProperty(prefix+"sfm_min_base", this.sfm_min_base+""); // double
properties.setProperty(prefix+"force_ref_dsi", this.force_ref_dsi + ""); // boolean
properties.setProperty(prefix+"force_orientations", this.force_orientations + ""); // boolean
properties.setProperty(prefix+"run_ly", this.run_ly + ""); // boolean
......@@ -1755,13 +1769,11 @@ public class IntersceneMatchParameters {
if (properties.getProperty(prefix+"ims_rebuild")!=null) this.ims_rebuild=Boolean.parseBoolean(properties.getProperty(prefix+"ims_rebuild"));
if (properties.getProperty(prefix+"ims_offset")!=null) this.ims_offset=Double.parseDouble(properties.getProperty(prefix+"ims_offset"));
if (properties.getProperty(prefix+"gmt_plus")!=null) this.gmt_plus=Double.parseDouble(properties.getProperty(prefix+"gmt_plus"));
if (properties.getProperty(prefix+"ims_ortho")!=null) this.ims_ortho= IntersceneMatchParameters.StringToDoubles(properties.getProperty(prefix+"ims_ortho"),4);
if (properties.getProperty(prefix+"ims_mount_atr")!=null) this.ims_mount_atr= IntersceneMatchParameters.StringToDoubles(properties.getProperty(prefix+"ims_mount_atr"),3);
if (properties.getProperty(prefix+"ims_mount_xyz")!=null) this.ims_mount_xyz= IntersceneMatchParameters.StringToDoubles(properties.getProperty(prefix+"ims_mount_xyz"),3);
if (properties.getProperty(prefix+"sfm_use")!=null) this.sfm_use=Boolean.parseBoolean(properties.getProperty(prefix+"sfm_use"));
if (properties.getProperty(prefix+"sfm_min_base")!=null) this.sfm_min_base=Double.parseDouble(properties.getProperty(prefix+"sfm_min_base"));
if (properties.getProperty(prefix+"force_ref_dsi")!=null) this.force_ref_dsi=Boolean.parseBoolean(properties.getProperty(prefix+"force_ref_dsi"));
if (properties.getProperty(prefix+"force_orientations")!=null) this.force_orientations=Boolean.parseBoolean(properties.getProperty(prefix+"force_orientations"));
if (properties.getProperty(prefix+"run_ly")!=null) this.run_ly=Boolean.parseBoolean(properties.getProperty(prefix+"run_ly"));
......@@ -2097,11 +2109,11 @@ public class IntersceneMatchParameters {
imp.ims_rebuild = this.ims_rebuild;
imp.ims_offset = this.ims_offset;
imp.gmt_plus = this.gmt_plus;
imp.ims_ortho = this.ims_ortho.clone();
imp.ims_mount_atr = this.ims_mount_atr.clone();
imp.ims_mount_xyz = this.ims_mount_xyz.clone();
imp.sfm_use = this.sfm_use;
imp.sfm_min_base = this.sfm_min_base;
imp.force_ref_dsi = this.force_ref_dsi;
imp.force_orientations = this.force_orientations;
imp.run_ly = this.run_ly;
......
......@@ -3412,7 +3412,7 @@ public class OpticalFlow {
return dsrbg_out;
}
@Deprecated
public void adjustPairsDualPass(
CLTParameters clt_parameters,
double k_prev,
......@@ -4812,19 +4812,33 @@ public class OpticalFlow {
}
// Build initial orientations
boolean ims_use = clt_parameters.imp.ims_use;
if (force_initial_orientations && !reuse_video) {
boolean OK = Interscene.setInitialOrientations(
clt_parameters, // final CLTParameters clt_parameters,
colorProcParameters, // final ColorProcParameters colorProcParameters,
quadCLTs, // final QuadCLT[] quadCLTs, //
ref_index, // final int ref_index,
set_channels, // final QuadCLT.SetChannels [] set_channels,
batch_mode, // final boolean batch_mode,
earliest_scene, // int earliest_scene,
start_ref_pointers, // int [] start_ref_pointers, // [0] - earliest valid scene, [1] ref_index
threadsMax, // final int threadsMax,
updateStatus, // final boolean updateStatus,
debugLevel); // final int debugLevel)
boolean OK =ims_use ?
Interscene.setInitialOrientationsIms(
clt_parameters, // final CLTParameters clt_parameters,
colorProcParameters, // final ColorProcParameters colorProcParameters,
quadCLTs, // final QuadCLT[] quadCLTs, //
ref_index, // final int ref_index,
set_channels, // final QuadCLT.SetChannels [] set_channels,
batch_mode, // final boolean batch_mode,
earliest_scene, // int earliest_scene,
start_ref_pointers, // int [] start_ref_pointers, // [0] - earliest valid scene, [1] ref_index
threadsMax, // final int threadsMax,
updateStatus, // final boolean updateStatus,
debugLevel): // final int debugLevel)
Interscene.setInitialOrientations(
clt_parameters, // final CLTParameters clt_parameters,
colorProcParameters, // final ColorProcParameters colorProcParameters,
quadCLTs, // final QuadCLT[] quadCLTs, //
ref_index, // final int ref_index,
set_channels, // final QuadCLT.SetChannels [] set_channels,
batch_mode, // final boolean batch_mode,
earliest_scene, // int earliest_scene,
start_ref_pointers, // int [] start_ref_pointers, // [0] - earliest valid scene, [1] ref_index
threadsMax, // final int threadsMax,
updateStatus, // final boolean updateStatus,
debugLevel); // final int debugLevel)
if (!OK) {
return null;
}
......@@ -4863,25 +4877,53 @@ public class OpticalFlow {
if ((quadCLTs[ref_index].getNumAccum() < min_num_interscene) &&
((quadCLTs[ref_index].getNumAccum() < quadCLTs[ref_index].getNumOrient())||
(quadCLTs[ref_index].getNumOrient() >= min_num_orient))) {
// should skip scenes w/o orientation 06/29/2022
combo_dsn_final = intersceneExport( // result indexed by COMBO_DSN_TITLES, COMBO_DSN_INDX_***
clt_parameters, // CLTParameters clt_parameters,
ers_reference, // ErsCorrection ers_reference,
quadCLTs, // QuadCLT [] scenes,
colorProcParameters, // ColorProcParameters colorProcParameters,
debugLevel); // int debug_level
if (clt_parameters.imp.sky_recalc) { // force blue sky recalculation even if it exists
reuseRefDSI(
clt_parameters, // CLTParameters clt_parameters,
colorProcParameters, // ColorProcParameters colorProcParameters,
quadCLT_main, // QuadCLT quadCLT_main, // tiles should be set
quadCLTs[ref_index], // QuadCLT quadCLT_ref, // tiles should be set
batch_mode, // final boolean batch_mode,
threadsMax, // final int threadsMax,
updateStatus, // final boolean updateStatus,
debugLevel); // int debugLevel)
boolean done_sfm = false;
if (quadCLTs[ref_index].getNumAccum() > 0) {
double mb_max_gain = clt_parameters.imp.mb_max_gain;
if (quadCLTs[ref_index].getNumOrient() < (min_num_orient - 1)) {
mb_max_gain = clt_parameters.imp.mb_max_gain_inter;
}
done_sfm = StructureFromMotion.sfmPair(
clt_parameters, // final CLTParameters clt_parameters,
quadCLTs[ref_index], // final QuadCLT ref_scene,
quadCLTs[earliest_scene], // final QuadCLT scene,
mb_max_gain, // double mb_max_gain,
batch_mode, // final boolean batch_mode,
debugLevel); // final int debugLevel)
QuadCLT[] scenes_pair = new QuadCLT[]{
quadCLTs[ref_index - 1],
quadCLTs[earliest_scene]};
StructureFromMotion.sfmPair(
clt_parameters, // final CLTParameters clt_parameters,
quadCLTs[ref_index], // final QuadCLT ref_scene,
scenes_pair, // final QuadCLT [] scenes,
mb_max_gain, // double mb_max_gain,
batch_mode, // final boolean batch_mode,
debugLevel); // final int debugLevel)
}
if (!done_sfm) { // first pass or sfm failed
// should skip scenes w/o orientation 06/29/2022
combo_dsn_final = intersceneExport( // result indexed by COMBO_DSN_TITLES, COMBO_DSN_INDX_***
clt_parameters, // CLTParameters clt_parameters,
ers_reference, // ErsCorrection ers_reference,
quadCLTs, // QuadCLT [] scenes,
colorProcParameters, // ColorProcParameters colorProcParameters,
debugLevel); // int debug_level
if (clt_parameters.imp.sky_recalc) { // force blue sky recalculation even if it exists
reuseRefDSI(
clt_parameters, // CLTParameters clt_parameters,
colorProcParameters, // ColorProcParameters colorProcParameters,
quadCLT_main, // QuadCLT quadCLT_main, // tiles should be set
quadCLTs[ref_index], // QuadCLT quadCLT_ref, // tiles should be set
batch_mode, // final boolean batch_mode,
threadsMax, // final int threadsMax,
updateStatus, // final boolean updateStatus,
debugLevel); // int debugLevel)
}
}
......@@ -6829,7 +6871,7 @@ public class OpticalFlow {
}
return szxy;
}
@Deprecated
public void adjustSeries(
CLTParameters clt_parameters,
double k_prev,
......@@ -7689,9 +7731,15 @@ public class OpticalFlow {
sx2 += wx * dt;
for (int m = 0; m < sy.length; m++) {
for (int d = 0; d < sy[m].length; d++) {
double y = scenes_xyzatr[ns][m][d];
sy [m][d] += w * y;
sxy[m][d] += wx * y;
if (scenes_xyzatr[ns]== null ) {
System.out.println("getVelocitiesFromScenes():scenes_xyzatr["+ns+"]== null");
}else if (scenes_xyzatr[ns][m]== null ) {
System.out.println("getVelocitiesFromScenes():scenes_xyzatr["+ns+"]["+m+"] == null");
} else {
double y = scenes_xyzatr[ns][m][d];
sy [m][d] += w * y;
sxy[m][d] += wx * y;
}
}
}
nds++; // number of different timestamps used
......
This diff is collapsed.
......@@ -3050,25 +3050,27 @@ public class TexturedModel {
hdr_whs[0], // int width,
wh, // int [] wh, // should be initialized to int [2]
full_render); // double [][] img_src)
scenes[ref_index].saveDoubleArrayInModelDirectory(
suffix+"-CROP", // String suffix,
null, // String [] labels, // or null
img_cropped, // double [][] data,
wh[0], // int width, // int tilesX,
wh[1]); // int height, // int tilesY,
// for (int tex_palette = tex_palette_start; tex_palette <= tex_palette_end; tex_palette++) {
for (int tex_palette: tex_pals) {
// try with fixed range?
double [] minmax = tex_um_fixed ? (new double[] {-tex_um_range/2, tex_um_range/2}): null;
scenes[ref_index].writeLwirPreview(
clt_parameters, // final CLTParameters clt_parameters,
img_cropped[0], // double [] data,
if (img_cropped != null) {
scenes[ref_index].saveDoubleArrayInModelDirectory(
suffix+"-CROP", // String suffix,
null, // String [] labels, // or null
img_cropped, // double [][] data,
wh[0], // int width, // int tilesX,
minmax, // double [] minmax, // null for auto
null, // QuadCLT scene,
tex_palette, // int tex_palette,
suffix+"-CROP"+"-PAL"+tex_palette, // +tex_palette, // String suffix,
debugLevel); // int debugLevel)
wh[1]); // int height, // int tilesY,
// for (int tex_palette = tex_palette_start; tex_palette <= tex_palette_end; tex_palette++) {
for (int tex_palette: tex_pals) {
// try with fixed range?
double [] minmax = tex_um_fixed ? (new double[] {-tex_um_range/2, tex_um_range/2}): null;
scenes[ref_index].writeLwirPreview(
clt_parameters, // final CLTParameters clt_parameters,
img_cropped[0], // double [] data,
wh[0], // int width, // int tilesX,
minmax, // double [] minmax, // null for auto
null, // QuadCLT scene,
tex_palette, // int tex_palette,
suffix+"-CROP"+"-PAL"+tex_palette, // +tex_palette, // String suffix,
debugLevel); // int debugLevel)
}
}
}
}
......
......@@ -8396,7 +8396,7 @@ if (debugLevel > -100) return true; // temporarily !
System.out.println("End of test");
}
@Deprecated
public void interSeriesLMA(
QuadCLT quadCLT_main, // tiles should be set
int ref_index, // -1 - last
......
......@@ -160,10 +160,13 @@ public class Render3D {
}
}
}
if (xy_min_max == null) {
return null;
}
marg_top = xy_min_max[1][0];
marg_left = xy_min_max[0][0];
marg_bottom = height - 1 - xy_min_max[1][1];
marg_right = width - 1 - xy_min_max[0][1];
marg_right = width - 1 - xy_min_max[0][1];
}
marg_top += crop_extra;
marg_left += crop_extra;
......
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