Commit adf9d12f authored by Andrey Filippov's avatar Andrey Filippov

monochrome mode of the tile processor - debugging

parent 37198376
......@@ -1562,12 +1562,12 @@ public class ImageDtt {
final int numcol = 3; // number of colors // keep the same, just do not use [0] and [1], [2] - green
final int nChn = image_data[0].length;
// final int numColors = image_data[0].length;
final int height=image_data[0][0].length/width;
final int tilesX=width/transform_size;
final int tilesY=height/transform_size;
final int nTilesInChn=tilesX*tilesY;
final double [][][][][][] clt_data = new double[quad][nChn][tilesY][tilesX][][];
final double [][][][][][] clt_data = new double[quad][numcol][tilesY][tilesX][][];
final Thread[] threads = newThreadArray(threadsMax);
final AtomicInteger ai = new AtomicInteger(0);
final double [] col_weights= new double [numcol]; // colors are RBG
......@@ -1603,6 +1603,9 @@ public class ImageDtt {
}
}
final int first_color = isMonochrome()? MONO_CHN : 0; // color that is non-zero
// reducing weight of on-axis correlation values to enhance detection of vertical/horizontal lines
// multiply correlation results inside the horizontal center strip 2*enhortho_width - 1 wide by enhortho_scale
......@@ -1905,9 +1908,10 @@ public class ImageDtt {
);
}
// See if macro_mode uses color channels for non-color?
for (int chn = 0; chn <numcol; chn++) if (!isMonochrome() || (chn == MONO_CHN) || macro_mode) { // in monochrome mode skip all non-mono (green) channels
boolean debug_for_fpga = FPGA_COMPARE_DATA && (globalDebugLevel > 0) && (tileX == debug_tileX) && (tileY == debug_tileY) && (chn == 2);
if ((globalDebugLevel > -1) && (tileX == debug_tileX) && (tileY == debug_tileY) && (chn == 2)) {
for (int ncol = 0; ncol <numcol; ncol++) {
if (!isMonochrome() || (ncol == MONO_CHN) || macro_mode) { // in monochrome mode skip all non-mono (green) channels
boolean debug_for_fpga = FPGA_COMPARE_DATA && (globalDebugLevel > 0) && (tileX == debug_tileX) && (tileY == debug_tileY) && (ncol == 2);
if ((globalDebugLevel > -1) && (tileX == debug_tileX) && (tileY == debug_tileY) && (ncol == 2)) {
System.out.println("\nUsing "+(macro_mode?"MACRO":"PIXEL")+" mode, centerX="+centerX+", centerY="+centerY);
System.out.println(disparity_array[tileY][tileX]+"\t"+
centersXY[0][0]+"\t"+centersXY[0][1]+"\t"+
......@@ -1921,7 +1925,7 @@ public class ImageDtt {
double [][] fpga_clt_data = new double [4][];
double [] fpga_fract_shiftsXY;
double [] fpga_centersXY = {centersXY[i][0],centersXY[i][1]};
int fpga_chn = chn; // ==2, green
int fpga_chn = ncol; // ==2, green
// round to nearest 1/128 pix (supported by FPGA code)
System.out.println(String.format("Center X= %f, center Y = %f", fpga_centersXY[0],fpga_centersXY[1]));
for (int j=0; j<2;j++){
......@@ -1934,16 +1938,16 @@ public class ImageDtt {
}
// fpga_centersXY[0]+=0.5; // half pixel shift horizontal zero pixel shift vertical
// fpga_centersXY[1]+=0.5; // half pixel shift vertical, zero pixel shift horizontal
// fpga_centersXY[0]+=0.5; // half pixel shift horizontal zero pixel shift vertical
// fpga_centersXY[1]+=0.5; // half pixel shift vertical, zero pixel shift horizontal
// fpga_centersXY[0]+=1.0; //
// fpga_centersXY[0]+=1.0; //
fpga_chn = 2;
System.out.println(String.format("Manually changing offset: center X= %f, center Y = %f", fpga_centersXY[0],fpga_centersXY[1]));
System.out.println(String.format("Manually changing color to %d (was %d)", fpga_chn, chn));
System.out.println(String.format("Manually changing color to %d (was %d)", fpga_chn, ncol));
......@@ -1976,7 +1980,7 @@ public class ImageDtt {
true); // debug
for (int im = 0; im < 4; im++) dbg_tile[im]=fpga_clt_data[im];
sdfa_instance.showArrays(dbg_tile, transform_size, transform_size, true, "f-shifted_x"+tileX+"_y"+tileY+"-z", titles);
System.out.println("Debugging for FPGA data, globalDebugLevel = "+globalDebugLevel+", tileX="+tileX+", tileY="+tileY+", sesnlor="+i+", color="+chn);
System.out.println("Debugging for FPGA data, globalDebugLevel = "+globalDebugLevel+", tileX="+tileX+", tileY="+tileY+", sesnlor="+i+", color="+ncol);
System.out.println("Debugging for FPGA data, fpga_fract_shiftsXY[0] = "+fpga_fract_shiftsXY[0]+", fpga_fract_shiftsXY[1]="+fpga_fract_shiftsXY[1]);
System.out.println();
......@@ -1997,7 +2001,7 @@ public class ImageDtt {
System.out.println(String.format("// DTT rotated, shift_x=%f. shift_y = %f", fpga_fract_shiftsXY[0],fpga_fract_shiftsXY[1]));
System.out.println(String.format("// DTT rotated range: %f ... %f", fpga_dtt_lim[1], fpga_dtt_lim[0]));
// scale = (1 << (FPGA_DTT_IN - 9)); // -1;
// scale = (1 << (FPGA_DTT_IN - 9)); // -1;
for (int dct_mode = 0; dct_mode <4; dct_mode++) {
for (int j = 0; j < 64; j++){
int id = (int) Math.round(scale * fpga_clt_data[dct_mode][j]);
......@@ -2008,39 +2012,39 @@ public class ImageDtt {
}
} // end of debug_for_fpga
clt_data[i][chn][tileY][tileX] = new double [4][];
clt_data[i][ncol][tileY][tileX] = new double [4][];
fract_shiftsXY[i] = extract_correct_tile( // return a pair of residual offsets
image_data[i],
width, // image width
(clt_kernels == null) ? null : clt_kernels[i], // [color][tileY][tileX][band][pixel]
clt_data[i][chn][tileY][tileX], //double [][] clt_tile, // should be double [4][];
clt_data[i][ncol][tileY][tileX], //double [][] clt_tile, // should be double [4][];
kernel_step,
transform_size,
dtt,
chn,
ncol,
centersXY[i][0], // centerX, // center of aberration-corrected (common model) tile, X
centersXY[i][1], // centerY, //
(!FPGA_COMPARE_DATA && (globalDebugLevel > -1) && (tileX == debug_tileX) && (tileY == debug_tileY) && (chn == 2) && (i==0)) ? (globalDebugLevel + 0) : 0, // external tile compare
(!FPGA_COMPARE_DATA && (globalDebugLevel > -1) && (tileX == debug_tileX) && (tileY == debug_tileY) && (ncol == 2) && (i==0)) ? (globalDebugLevel + 0) : 0, // external tile compare
no_deconvolution,
false, // ); // transpose);
((saturation_imp != null) ? saturation_imp[i] : null), //final boolean [][] saturation_imp, // (near) saturated pixels or null
((saturation_imp != null) ? overexp_all: null)); // final double [] overexposed)
} // for (int i = 0; i < quad; i++)
if ((globalDebugLevel > -1) && (tileX == debug_tileX) && (tileY == debug_tileY) && (chn == 2)) {
if ((globalDebugLevel > -1) && (tileX == debug_tileX) && (tileY == debug_tileY) && (ncol == 2)) {
System.out.println();
}
if ((globalDebugLevel > 0) && (debug_tileX == tileX) && (debug_tileY == tileY) && (chn == 2) && !FPGA_COMPARE_DATA) {
if ((globalDebugLevel > 0) && (debug_tileX == tileX) && (debug_tileY == tileY) && (ncol == 2) && !FPGA_COMPARE_DATA) {
ShowDoubleFloatArrays sdfa_instance = new ShowDoubleFloatArrays(); // just for debugging?
String [] titles = {"CC0","SC0","CS0","SS0","CC1","SC1","CS1","SS1","CC2","SC2","CS2","SS2","CC3","SC3","CS3","SS3"};
double [][] dbg_tile = new double [16][];
for (int i = 0; i < 16; i++) dbg_tile[i]=clt_data[i>>2][chn][tileY][tileX][i & 3];
for (int i = 0; i < 16; i++) dbg_tile[i]=clt_data[i>>2][ncol][tileY][tileX][i & 3];
sdfa_instance.showArrays(dbg_tile, transform_size, transform_size, true, "pre-shifted_x"+tileX+"_y"+tileY, titles);
}
if ((globalDebugLevel > 0) && (tileX >= debug_tileX - 2) && (tileX <= debug_tileX + 2) &&
(tileY >= debug_tileY - 2) && (tileY <= debug_tileY+2)) {
for (int i = 0; i < quad; i++) {
System.out.println("clt_aberrations_quad(): color="+chn+", tileX="+tileX+", tileY="+tileY+
System.out.println("clt_aberrations_quad(): color="+ncol+", tileX="+tileX+", tileY="+tileY+
" fract_shiftsXY["+i+"][0]="+fract_shiftsXY[i][0]+" fract_shiftsXY["+i+"][1]="+fract_shiftsXY[i][1]);
}
}
......@@ -2049,13 +2053,13 @@ public class ImageDtt {
// apply residual shift
for (int i = 0; i < quad; i++) {
fract_shift( // fractional shift in transform domain. Currently uses sin/cos - change to tables with 2? rotations
clt_data[i][chn][tileY][tileX], // double [][] clt_tile,
clt_data[i][ncol][tileY][tileX], // double [][] clt_tile,
transform_size,
fract_shiftsXY[i][0], // double shiftX,
fract_shiftsXY[i][1], // double shiftY,
// (globalDebugLevel > 0) && (tileX == debug_tileX) && (tileY == debug_tileY)); // external tile compare
((globalDebugLevel > 1) &&
((chn==0) || isMonochrome()) &&
((ncol==0) || isMonochrome()) &&
(tileX >= debug_tileX - 2) && (tileX <= debug_tileX + 2) &&
(tileY >= debug_tileY - 2) && (tileY <= debug_tileY+2)));
}
......@@ -2063,14 +2067,19 @@ public class ImageDtt {
ShowDoubleFloatArrays sdfa_instance = new ShowDoubleFloatArrays(); // just for debugging?
String [] titles = {"CC0","SC0","CS0","SS0","CC1","SC1","CS1","SS1","CC2","SC2","CS2","SS2","CC3","SC3","CS3","SS3"};
double [][] dbg_tile = new double [16][];
for (int i = 0; i < 16; i++) dbg_tile[i]=clt_data[i>>2][chn][tileY][tileX][i & 3];
for (int i = 0; i < 16; i++) dbg_tile[i]=clt_data[i>>2][ncol][tileY][tileX][i & 3];
sdfa_instance.showArrays(dbg_tile, transform_size, transform_size, true, "shifted_x"+tileX+"_y"+tileY+"-z", titles);
}
}
} // end of for (int chn = 0; chn <numcol; chn++) if (!isMonochrome() || (chn == MONO_CHN) || macro_mode) { // in monochrome mode skip all non-mono (green) channels
} else { // if (!isMonochrome() || (chn == MONO_CHN) || macro_mode) { // in monochrome mode skip all non-mono (green) channels
for (int i = 0; i < quad; i++) {
clt_data[i][ncol] = null; // erase unused clt_data
}
}
}// end of for (int chn = 0; chn <numcol; chn++)
int tile_lma_debug_level = ((tileX == debug_tileX) && (tileY == debug_tileY))? imgdtt_params.lma_debug_level : -1;
......@@ -2438,7 +2447,7 @@ public class ImageDtt {
tcorr_partial = new double[quad][numcol+1][];
for (int pair = 0; pair < corr_pairs.length; pair++){
for (int ncol = 0; ncol <numcol; ncol++){
for (int ncol = 0; ncol <numcol; ncol++) if (clt_data[ncol] != null){
double [][] data1 = clt_data[corr_pairs[pair][0]][ncol][tileY][tileX];
double [][] data2 = clt_data[corr_pairs[pair][1]][ncol][tileY][tileX];
if ((data1 != null) && (data2 != null)) {
......@@ -2777,13 +2786,13 @@ public class ImageDtt {
(globalDebugLevel > 0) && debugTile);
// mix RGB from iclt_tile, mix alpha with - what? correlation strength or 'don't care'? good correlation or all > min?
for (int i = 0; i < iclt_tile[0][0].length; i++ ) {
for (int i = 0; i < iclt_tile[0][first_color].length; i++ ) {
double sw = 0.0;
for (int ip = 0; ip < quad; ip++) {
sw += texture_tiles[tileY][tileX][numcol+1+ip][i];
}
if (sw != 0 ) sw = 1.0/sw;
for (int ncol = 0; ncol < numcol; ncol++) { // color
for (int ncol = 0; ncol < numcol; ncol++) if (iclt_tile[0][ncol] !=null){ // color
texture_tiles[tileY][tileX][ncol][i] = 0.0; //iclt[tileY][tileX][chn]
for (int ip = 0; ip < quad; ip++) {
texture_tiles[tileY][tileX][ncol][i] += sw * texture_tiles[tileY][tileX][numcol+1+ip][i] * iclt_tile[ip][ncol][i];
......@@ -4210,6 +4219,10 @@ public class ImageDtt {
final int threadsMax, // maximal number of threads to launch
final int globalDebugLevel)
{
if (clt_data == null) {
System.out.println("clt_lpf(): clt_data=null");
return;
}
final int tilesY=clt_data.length;
final int tilesX=clt_data[0].length;
final int nTiles=tilesX*tilesY;
......@@ -4922,11 +4935,19 @@ public class ImageDtt {
chn_kernel = clt_kernels.length - 1;
}
int chn_img = chn;
// use zero kernel channel for monochrome images
if (image_data.length <= chn_img) {
chn_img = image_data.length - 1;
}
// now for mono both image_data and clt_kernel have outer dimension of 1, but chn == 2 (green)
boolean use_kernels = (clt_kernels != null) && !dbg_no_deconvolution;
boolean bdebug0 = debugLevel > 0;
boolean bdebug = debugLevel > 1;
double [] residual_shift = new double[2];
int height = image_data[0].length/width;
int height = image_data[chn_img].length/width;
int transform_size2 = 2* transform_size;
// if (dtt == null) dtt = new DttRad2(transform_size); should have window set up
double [] tile_in = new double [4*transform_size*transform_size];
......@@ -4954,7 +4975,7 @@ public class ImageDtt {
px = centerX - transform_size - (ce.data_x + ce.dxc_dx * kdx + ce.dxc_dy * kdy) ; // fractional left corner
py = centerY - transform_size - (ce.data_y + ce.dyc_dx * kdx + ce.dyc_dy * kdy) ; // fractional top corner
if (debug_gpu) {
System.out.println("========= Color channel "+chn+" , kernel channel "+chn_kernel+" =============");
System.out.println("========= Color channel "+chn+" , kernel channel "+chn_kernel+" input image channel="+chn_img+" =============");
System.out.println("ce.data_x="+ce.data_x+", ce.data_y="+ce.data_y);
System.out.println("ce.center_x="+ce.center_x+", ce.center_y="+ce.center_y);
System.out.println("ce.dxc_dx="+ce.dxc_dx+", ce.dxc_dy="+ce.dxc_dy);
......@@ -4987,7 +5008,7 @@ public class ImageDtt {
if ((ctile_left >= 0) && (ctile_left < (width - transform_size2)) &&
(ctile_top >= 0) && (ctile_top < (height - transform_size2))) {
for (int i = 0; i < transform_size2; i++){
System.arraycopy(image_data[chn], (ctile_top + i) * width + ctile_left, tile_in, transform_size2 * i, transform_size2);
System.arraycopy(image_data[chn_img], (ctile_top + i) * width + ctile_left, tile_in, transform_size2 * i, transform_size2);
}
} else { // copy by 1
for (int i = 0; i < transform_size2; i++){
......@@ -4998,12 +5019,12 @@ public class ImageDtt {
int pj = ctile_left + j;
if (pj < 0) pj &= 1;
else if (pj >= width) pj = width - 2 + (pj & 1);
tile_in[transform_size2 * i + j] = image_data[chn][pi * width + pj];
tile_in[transform_size2 * i + j] = image_data[chn_img][pi * width + pj];
}
}
}
if (debug_gpu) {
System.out.println("---Image tile for color="+chn+"---");
System.out.println("---Image tile for color="+chn_img+"---");
for (int i = 0; i < transform_size2; i++) {
for (int j = 0; j < transform_size2; j++) {
System.out.print(String.format("%10.5f ", tile_in[transform_size2 * i + j]));
......
......@@ -3224,6 +3224,7 @@ public class QuadCLT {
boolean lwir_subtract_dc = colorProcParameters.lwir_subtract_dc;
boolean lwir_eq_chn = colorProcParameters.lwir_eq_chn;
boolean correct_vignetting = colorProcParameters.correct_vignetting;
this.is_mono = is_lwir; // maybe add other monochrome?
for (int srcChannel=0; srcChannel < channelFiles.length; srcChannel++){
int nFile=channelFiles[srcChannel]; // channelFiles[srcChannel];
......@@ -3256,10 +3257,10 @@ public class QuadCLT {
float [] pixels=(float []) imp_srcs[srcChannel].getProcessor().getPixels();
int width = imp_srcs[srcChannel].getWidth();
int height = imp_srcs[srcChannel].getHeight();
if (debugLevel > -1) {
if ((debugLevel > -1) && (!isMonochrome())) {
double [] max_pix= {0.0, 0.0, 0.0, 0.0};
// for (int y = 0; y < height-1; y+=2){
for (int y = 0; y < 499; y+=2){
for (int y = 0; (y < 499) && (y < height); y+=2){
// for (int x = 0; x < width-1; x+=2){
for (int x = width/2; x < width-1; x+=2){
if (pixels[y*width+x ] > max_pix[0]) max_pix[0] = pixels[y*width+x ];
......@@ -3373,7 +3374,7 @@ public class QuadCLT {
}
if ((debugLevel > -1) && (saturation_imp != null)){
if ((debugLevel > -1) && (saturation_imp != null) && !is_lwir){
String [] titles = {"chn0","chn1","chn2","chn3"};
double [][] dbg_satur = new double [saturation_imp.length] [saturation_imp[0].length];
for (int srcChannel=0; srcChannel<channelFiles.length; srcChannel++){
......@@ -3385,7 +3386,7 @@ public class QuadCLT {
int height = imp_srcs[0].getHeight();
(new ShowDoubleFloatArrays()).showArrays(dbg_satur, width, height, true, "Saturated" , titles);
if (debugLevel > -1) { // 0){
if ((debugLevel > -1) && !isMonochrome()) { // 0){
double [][] dbg_dpixels_norm = new double [channelFiles.length][];
for (int srcChannel=0; srcChannel<channelFiles.length; srcChannel++){
float [] pixels=(float []) imp_srcs[srcChannel].getProcessor().getPixels();
......@@ -3444,7 +3445,6 @@ public class QuadCLT {
}
this.lwir_offset /= num_avg;
}
this.is_mono = is_lwir; // maybe add other monochrome?
return imp_srcs;
}
......@@ -3793,7 +3793,8 @@ public class QuadCLT {
for (int srcChannel=0; srcChannel < channelFiles.length; srcChannel++){
int nFile=channelFiles[srcChannel];
if (nFile >=0) {
offsets[srcChannel]= (avr_pix[srcChannel][0] - (remove_dc ? 0.0: avg));
// offsets[srcChannel]= (avr_pix[srcChannel][0] - (remove_dc ? 0.0: avg));
offsets[srcChannel]= avr_pix[srcChannel][0];
float fd = (float)offsets[srcChannel];
float [] pixels = (float []) imp_srcs[srcChannel].getProcessor().getPixels();
for (int i = 0; i < pixels.length; i++) {
......@@ -3853,7 +3854,7 @@ public class QuadCLT {
}
} else {
for (int j =0 ; j < double_stacks[i][0].length; j++){
double_stacks[i][0][j]*=0.25; // Scale mono by 1/4 - to have the same overall "gain" as for bayer
double_stacks[i][0][j]*=1.0; // Scale mono by 1/4 - to have the same overall "gain" as for bayer
}
}
}
......@@ -3979,10 +3980,11 @@ public class QuadCLT {
// (clt_parameters.dbg_mode & 256) != 0, // transpose convolve
threadsMax,
debugLevel);
int first_color = isMonochrome()? ImageDtt.MONO_CHN:0;
if (debugLevel > -1){
System.out.println("clt_data.length="+clt_data.length+" clt_data[0].length="+clt_data[0].length
+" clt_data[0][0].length="+clt_data[0][0].length+" clt_data[0][0][0].length="+
clt_data[0][0][0].length);
+" clt_data[0]["+first_color+"].length="+clt_data[0][first_color].length+" clt_data[0]["+first_color+"][0].length="+
clt_data[0][first_color][0].length);
}
// visualize texture tiles as RGBA slices
double [][] texture_nonoverlap = null;
......@@ -4029,7 +4031,7 @@ public class QuadCLT {
}
if (!batch_mode && clt_parameters.show_overlap) {
sdfa_instance.showArrays(
sdfa_instance.showArrays( // all but r-rms, b-rms
texture_overlap,
tilesX * clt_parameters.transform_size,
tilesY * clt_parameters.transform_size,
......@@ -4061,6 +4063,7 @@ public class QuadCLT {
}
}
// visualize correlation results
// bo-b3 non-zero, r*, g* - zero
if (clt_corr_combo!=null){
if (disparity_map != null){
if (!batch_mode && clt_parameters.show_map && (debugLevel > -1)){
......@@ -4215,7 +4218,7 @@ public class QuadCLT {
threadsMax,
debugLevel);
}
// all zeros
sdfa_instance.showArrays(
corr_rslt,
tilesX*(2*clt_parameters.transform_size),
......@@ -4240,13 +4243,15 @@ public class QuadCLT {
clt_parameters.corr_border_contrast,
threadsMax,
debugLevel);
sdfa_instance.showArrays(
// titles.length = 15, corr_rslt_partial.length=16!
System.out.println("corr_rslt_partial.length = "+corr_rslt_partial.length+", titles.length = "+titles.length);
sdfa_instance.showArrays( // out of boundary 15
corr_rslt_partial,
tilesX*(2*clt_parameters.transform_size),
tilesY*(2*clt_parameters.transform_size),
true,
name+"-PART_CORR-D"+clt_parameters.disparity,
titles);
name+"-PART_CORR-D"+clt_parameters.disparity);
// titles);
}
}
}
......@@ -4259,7 +4264,7 @@ public class QuadCLT {
// String titleFull=title+"-SPLIT-D"+clt_parameters.disparity;
if (clt_parameters.corr_sigma > 0){ // no filter at all
for (int chn = 0; chn < clt_data[iQuad].length; chn++) {
for (int chn = 0; chn < clt_data[iQuad].length; chn++) if (clt_data[iQuad][chn] != null){
image_dtt.clt_lpf(
clt_parameters.corr_sigma,
clt_data[iQuad][chn],
......@@ -4275,7 +4280,7 @@ public class QuadCLT {
}
if (!batch_mode && (debugLevel > 0)){
double [][] clt = new double [clt_data[iQuad].length*4][];
for (int chn = 0; chn < clt_data[iQuad].length; chn++) {
for (int chn = 0; chn < clt_data[iQuad].length; chn++) if (clt_data[iQuad][chn] != null){
double [][] clt_set = image_dtt.clt_dbg(
clt_data [iQuad][chn],
threadsMax,
......@@ -4292,7 +4297,7 @@ public class QuadCLT {
}
}
double [][] iclt_data = new double [clt_data[iQuad].length][];
for (int chn=0; chn<iclt_data.length;chn++){
for (int chn=0; chn<iclt_data.length;chn++) if (clt_data[iQuad][chn] != null) {
iclt_data[chn] = image_dtt.iclt_2d(
clt_data[iQuad][chn], // scanline representation of dcd data, organized as dct_size x dct_size tiles
clt_parameters.transform_size, // final int
......@@ -4509,11 +4514,11 @@ public class QuadCLT {
mx,
255.0);
rbg_in = new double [3][iclt_data[green_index].length];
for (int i = 0; i < rbg_in.length; i++) {
for (int i = 0; i < rbg_in[0].length; i++) {
double [] rgb = tc.getRGB(iclt_data[green_index][i]);
rbg_in[i][0] = rgb[0]; // red
rbg_in[i][1] = rgb[2]; // blue
rbg_in[i][2] = rgb[1]; // green
rbg_in[0][i] = rgb[0]; // red
rbg_in[1][i] = rgb[2]; // blue
rbg_in[2][i] = rgb[1]; // green
}
}
......
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