Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
imagej-elphel
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
3
Issues
3
List
Board
Labels
Milestones
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Elphel
imagej-elphel
Commits
2eae496d
Commit
2eae496d
authored
Aug 25, 2020
by
Andrey Filippov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
trying group phase correlation, preparing to convert to GPU
parent
a149cadd
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
485 additions
and
82 deletions
+485
-82
CLTParameters.java
src/main/java/com/elphel/imagej/cameras/CLTParameters.java
+9
-3
GPUTileProcessor.java
src/main/java/com/elphel/imagej/gpu/GPUTileProcessor.java
+21
-1
Correlation2d.java
...n/java/com/elphel/imagej/tileprocessor/Correlation2d.java
+260
-1
ImageDtt.java
src/main/java/com/elphel/imagej/tileprocessor/ImageDtt.java
+43
-62
ImageDttCPU.java
...ain/java/com/elphel/imagej/tileprocessor/ImageDttCPU.java
+67
-6
ImageDttParameters.java
...a/com/elphel/imagej/tileprocessor/ImageDttParameters.java
+67
-3
QuadCLT.java
src/main/java/com/elphel/imagej/tileprocessor/QuadCLT.java
+17
-5
QuadCLTCPU.java
...main/java/com/elphel/imagej/tileprocessor/QuadCLTCPU.java
+1
-1
No files found.
src/main/java/com/elphel/imagej/cameras/CLTParameters.java
View file @
2eae496d
...
@@ -32,7 +32,7 @@ public class CLTParameters {
...
@@ -32,7 +32,7 @@ public class CLTParameters {
public
int
ishift_y
=
0
;
// debug feature - shift source image by this pixels down
public
int
ishift_y
=
0
;
// debug feature - shift source image by this pixels down
private
double
fat_zero
=
0.05
;
// modify phase correlation to prevent division by very small numbers
private
double
fat_zero
=
0.05
;
// modify phase correlation to prevent division by very small numbers
private
double
fat_zero_mono
=
0.
1
;
// modify phase correlation to prevent division by very small numbers
private
double
fat_zero_mono
=
0.
03
;
// modify phase correlation to prevent division by very small numbers
private
double
corr_sigma
=
0.8
;
// LPF correlation sigma
private
double
corr_sigma
=
0.8
;
// LPF correlation sigma
private
double
corr_sigma_mono
=
0.1
;
// LPF correlation sigma for monochrome images
private
double
corr_sigma_mono
=
0.1
;
// LPF correlation sigma for monochrome images
private
double
scale_strength_main
=
1.0
;
// leave as is
private
double
scale_strength_main
=
1.0
;
// leave as is
...
@@ -766,9 +766,10 @@ public class CLTParameters {
...
@@ -766,9 +766,10 @@ public class CLTParameters {
public
boolean
taEnMismatch
=
false
;
// Enable cost of a measurement layer not having same layer in the same location or near
public
boolean
taEnMismatch
=
false
;
// Enable cost of a measurement layer not having same layer in the same location or near
// gpu processing parameters
// gpu processing parameters
public
double
gpu_corr_scale
=
0.75
;
// reduce GPU-generated correlation values
public
int
gpu_corr_rad
=
7
;
// size of the correlation to save - initially only 15x15
public
int
gpu_corr_rad
=
7
;
// size of the correlation to save - initially only 15x15
public
double
gpu_weight_r
=
0.25
;
public
double
gpu_weight_r
=
0.
5
;
//
25;
public
double
gpu_weight_b
=
0.25
;
// weight g = 1.0 - gpu_weight_r - gpu_weight_b
public
double
gpu_weight_b
=
0.2
;
// 0.2
5; // weight g = 1.0 - gpu_weight_r - gpu_weight_b
public
double
gpu_sigma_r
=
0.9
;
// 1.1;
public
double
gpu_sigma_r
=
0.9
;
// 1.1;
public
double
gpu_sigma_b
=
0.9
;
// 1.1;
public
double
gpu_sigma_b
=
0.9
;
// 1.1;
public
double
gpu_sigma_g
=
0.6
;
// 0.7;
public
double
gpu_sigma_g
=
0.6
;
// 0.7;
...
@@ -1567,6 +1568,7 @@ public class CLTParameters {
...
@@ -1567,6 +1568,7 @@ public class CLTParameters {
properties
.
setProperty
(
prefix
+
"taEnMismatch"
,
this
.
taEnMismatch
+
""
);
properties
.
setProperty
(
prefix
+
"taEnMismatch"
,
this
.
taEnMismatch
+
""
);
properties
.
setProperty
(
prefix
+
"gpu_corr_scale"
,
this
.
gpu_corr_scale
+
""
);
properties
.
setProperty
(
prefix
+
"gpu_corr_rad"
,
this
.
gpu_corr_rad
+
""
);
properties
.
setProperty
(
prefix
+
"gpu_corr_rad"
,
this
.
gpu_corr_rad
+
""
);
properties
.
setProperty
(
prefix
+
"gpu_weight_r"
,
this
.
gpu_weight_r
+
""
);
properties
.
setProperty
(
prefix
+
"gpu_weight_r"
,
this
.
gpu_weight_r
+
""
);
properties
.
setProperty
(
prefix
+
"gpu_weight_b"
,
this
.
gpu_weight_b
+
""
);
properties
.
setProperty
(
prefix
+
"gpu_weight_b"
,
this
.
gpu_weight_b
+
""
);
...
@@ -2352,6 +2354,7 @@ public class CLTParameters {
...
@@ -2352,6 +2354,7 @@ public class CLTParameters {
if
(
properties
.
getProperty
(
prefix
+
"taEnFlaps"
)!=
null
)
this
.
taEnFlaps
=
Boolean
.
parseBoolean
(
properties
.
getProperty
(
prefix
+
"taEnFlaps"
));
if
(
properties
.
getProperty
(
prefix
+
"taEnFlaps"
)!=
null
)
this
.
taEnFlaps
=
Boolean
.
parseBoolean
(
properties
.
getProperty
(
prefix
+
"taEnFlaps"
));
if
(
properties
.
getProperty
(
prefix
+
"taEnMismatch"
)!=
null
)
this
.
taEnMismatch
=
Boolean
.
parseBoolean
(
properties
.
getProperty
(
prefix
+
"taEnMismatch"
));
if
(
properties
.
getProperty
(
prefix
+
"taEnMismatch"
)!=
null
)
this
.
taEnMismatch
=
Boolean
.
parseBoolean
(
properties
.
getProperty
(
prefix
+
"taEnMismatch"
));
if
(
properties
.
getProperty
(
prefix
+
"gpu_corr_scale"
)!=
null
)
this
.
gpu_corr_scale
=
Double
.
parseDouble
(
properties
.
getProperty
(
prefix
+
"gpu_corr_scale"
));
if
(
properties
.
getProperty
(
prefix
+
"gpu_corr_rad"
)!=
null
)
this
.
gpu_corr_rad
=
Integer
.
parseInt
(
properties
.
getProperty
(
prefix
+
"gpu_corr_rad"
));
if
(
properties
.
getProperty
(
prefix
+
"gpu_corr_rad"
)!=
null
)
this
.
gpu_corr_rad
=
Integer
.
parseInt
(
properties
.
getProperty
(
prefix
+
"gpu_corr_rad"
));
if
(
properties
.
getProperty
(
prefix
+
"gpu_weight_r"
)!=
null
)
this
.
gpu_weight_r
=
Double
.
parseDouble
(
properties
.
getProperty
(
prefix
+
"gpu_weight_r"
));
if
(
properties
.
getProperty
(
prefix
+
"gpu_weight_r"
)!=
null
)
this
.
gpu_weight_r
=
Double
.
parseDouble
(
properties
.
getProperty
(
prefix
+
"gpu_weight_r"
));
if
(
properties
.
getProperty
(
prefix
+
"gpu_weight_b"
)!=
null
)
this
.
gpu_weight_b
=
Double
.
parseDouble
(
properties
.
getProperty
(
prefix
+
"gpu_weight_b"
));
if
(
properties
.
getProperty
(
prefix
+
"gpu_weight_b"
)!=
null
)
this
.
gpu_weight_b
=
Double
.
parseDouble
(
properties
.
getProperty
(
prefix
+
"gpu_weight_b"
));
...
@@ -3279,6 +3282,8 @@ public class CLTParameters {
...
@@ -3279,6 +3282,8 @@ public class CLTParameters {
gd
.
addTab
(
"GPU"
,
"Parameters for GPU development"
);
gd
.
addTab
(
"GPU"
,
"Parameters for GPU development"
);
gd
.
addMessage
(
"--- GPU processing parameters ---"
);
gd
.
addMessage
(
"--- GPU processing parameters ---"
);
gd
.
addNumericField
(
"GPU 2D correlation scale"
,
this
.
gpu_corr_scale
,
4
,
6
,
""
,
"Reduce GPU-generated correlation values to approximately match CPU-generated ones"
);
gd
.
addNumericField
(
"Correlation radius"
,
this
.
gpu_corr_rad
,
0
,
6
,
"pix"
,
gd
.
addNumericField
(
"Correlation radius"
,
this
.
gpu_corr_rad
,
0
,
6
,
"pix"
,
"Size of the 2D correlation - maximal radius = 7 corresponds to full 15x15 pixel tile"
);
"Size of the 2D correlation - maximal radius = 7 corresponds to full 15x15 pixel tile"
);
gd
.
addNumericField
(
"Correlation weight R"
,
this
.
gpu_weight_r
,
4
,
6
,
""
,
gd
.
addNumericField
(
"Correlation weight R"
,
this
.
gpu_weight_r
,
4
,
6
,
""
,
...
@@ -4057,6 +4062,7 @@ public class CLTParameters {
...
@@ -4057,6 +4062,7 @@ public class CLTParameters {
this
.
taEnFlaps
=
gd
.
getNextBoolean
();
this
.
taEnFlaps
=
gd
.
getNextBoolean
();
this
.
taEnMismatch
=
gd
.
getNextBoolean
();
this
.
taEnMismatch
=
gd
.
getNextBoolean
();
this
.
gpu_corr_scale
=
gd
.
getNextNumber
();
this
.
gpu_corr_rad
=
(
int
)
gd
.
getNextNumber
();
this
.
gpu_corr_rad
=
(
int
)
gd
.
getNextNumber
();
this
.
gpu_weight_r
=
gd
.
getNextNumber
();
this
.
gpu_weight_r
=
gd
.
getNextNumber
();
this
.
gpu_weight_b
=
gd
.
getNextNumber
();
this
.
gpu_weight_b
=
gd
.
getNextNumber
();
...
...
src/main/java/com/elphel/imagej/gpu/GPUTileProcessor.java
View file @
2eae496d
...
@@ -803,7 +803,7 @@ public class GPUTileProcessor {
...
@@ -803,7 +803,7 @@ public class GPUTileProcessor {
/**
/**
* Copy array of CPU-prepared tasks to the GPU memory
* Copy array of CPU-prepared tasks to the GPU memory
* @param tile_tasks array of TpTask prepared by the CPU (before geometry correction is appl
l
ied)
* @param tile_tasks array of TpTask prepared by the CPU (before geometry correction is applied)
* @param use_aux Use second (aux) camera
* @param use_aux Use second (aux) camera
*/
*/
public
void
setTasks
(
TpTask
[]
tile_tasks
,
boolean
use_aux
)
// while is it in class member? - just to be able to free
public
void
setTasks
(
TpTask
[]
tile_tasks
,
boolean
use_aux
)
// while is it in class member? - just to be able to free
...
@@ -2078,6 +2078,16 @@ public class GPUTileProcessor {
...
@@ -2078,6 +2078,16 @@ public class GPUTileProcessor {
if
(
debug
)
System
.
out
.
println
(
"constantMemorySize: "
+
constantMemorySize
);
if
(
debug
)
System
.
out
.
println
(
"constantMemorySize: "
+
constantMemorySize
);
cuMemcpyHtoD
(
constantMemoryPointer
,
Pointer
.
to
(
lpf_flat
),
constantMemorySize
);
cuMemcpyHtoD
(
constantMemoryPointer
,
Pointer
.
to
(
lpf_flat
),
constantMemorySize
);
if
(
debug
)
System
.
out
.
println
();
if
(
debug
)
System
.
out
.
println
();
/*
if (debug) {
for (int i = 0; i < lpf_flat.length; i++) {
System.out.print(String.format("%8.5f", lpf_flat[i]));
if (((i+1) % 16) == 0) {
System.out.println();
}
}
}
*/
}
}
public
void
setLpfCorr
(
public
void
setLpfCorr
(
...
@@ -2094,6 +2104,16 @@ public class GPUTileProcessor {
...
@@ -2094,6 +2104,16 @@ public class GPUTileProcessor {
if
(
debug
)
System
.
out
.
println
(
"constantMemorySize: "
+
constantMemorySize
);
if
(
debug
)
System
.
out
.
println
(
"constantMemorySize: "
+
constantMemorySize
);
cuMemcpyHtoD
(
constantMemoryPointer
,
Pointer
.
to
(
lpf_flat
),
constantMemorySize
);
cuMemcpyHtoD
(
constantMemoryPointer
,
Pointer
.
to
(
lpf_flat
),
constantMemorySize
);
if
(
debug
)
System
.
out
.
println
();
if
(
debug
)
System
.
out
.
println
();
/*
if (debug) {
for (int i = 0; i < lpf_flat.length; i++) {
System.out.print(String.format("%8.5f", lpf_flat[i]));
if (((i+1) % 16) == 0) {
System.out.println();
}
}
}
*/
}
}
public
float
[]
floatSetCltLpfFd
(
public
float
[]
floatSetCltLpfFd
(
...
...
src/main/java/com/elphel/imagej/tileprocessor/Correlation2d.java
View file @
2eae496d
This diff is collapsed.
Click to expand it.
src/main/java/com/elphel/imagej/tileprocessor/ImageDtt.java
View file @
2eae496d
...
@@ -72,6 +72,7 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -72,6 +72,7 @@ public class ImageDtt extends ImageDttCPU {
final
float
[][][]
iclt_fimg
,
// will return quad images or null to skip, use quadCLT.linearStackToColor
final
float
[][][]
iclt_fimg
,
// will return quad images or null to skip, use quadCLT.linearStackToColor
//// final int width,
//// final int width,
// new parameters, will replace some other?
// new parameters, will replace some other?
final
double
gpu_corr_scale
,
// 0.75; // reduce GPU-generated correlation values
final
double
gpu_fat_zero
,
// clt_parameters.getGpuFatZero(is_mono);absolute == 30.0
final
double
gpu_fat_zero
,
// clt_parameters.getGpuFatZero(is_mono);absolute == 30.0
final
double
gpu_sigma_r
,
// 0.9, 1.1
final
double
gpu_sigma_r
,
// 0.9, 1.1
final
double
gpu_sigma_b
,
// 0.9, 1.1
final
double
gpu_sigma_b
,
// 0.9, 1.1
...
@@ -163,6 +164,7 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -163,6 +164,7 @@ public class ImageDtt extends ImageDttCPU {
}
}
*/
*/
// keep for now for mono, find out what do they mean for macro mode
// keep for now for mono, find out what do they mean for macro mode
if
(
macro_mode
)
{
// all the same as they now mean different
if
(
macro_mode
)
{
// all the same as they now mean different
//compensating Bayer correction
//compensating Bayer correction
col_weights
[
0
]
=
0.25
;
// 1.0/3;
col_weights
[
0
]
=
0.25
;
// 1.0/3;
...
@@ -179,7 +181,7 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -179,7 +181,7 @@ public class ImageDtt extends ImageDttCPU {
col_weights
[
1
]
=
corr_blue
*
col_weights
[
2
];
col_weights
[
1
]
=
corr_blue
*
col_weights
[
2
];
}
}
}
}
/*
double [] scales = isMonochrome() ?
double [] scales = isMonochrome() ?
(new double [] {1.0}) :
(new double [] {1.0}) :
(macro_mode?
(macro_mode?
...
@@ -189,13 +191,14 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -189,13 +191,14 @@ public class ImageDtt extends ImageDttCPU {
corr_blue, // 0.25
corr_blue, // 0.25
1.0 - corr_red - corr_blue})); // 0.5
1.0 - corr_red - corr_blue})); // 0.5
*/
final
int
corr_size
=
transform_size
*
2
-
1
;
final
int
corr_size
=
transform_size
*
2
-
1
;
final
int
[][]
transpose_indices
=
new
int
[
corr_size
*(
corr_size
-
1
)/
2
][
2
];
//
final int [][] transpose_indices = new int [corr_size*(corr_size-1)/2][2];
if
((
globalDebugLevel
>
-
10
)
&&
(
disparity_corr
!=
0.0
)){
if
((
globalDebugLevel
>
-
10
)
&&
(
disparity_corr
!=
0.0
)){
System
.
out
.
println
(
String
.
format
(
"Using manual infinity disparity correction of %8.5f pixels"
,
disparity_corr
));
System
.
out
.
println
(
String
.
format
(
"Using manual infinity disparity correction of %8.5f pixels"
,
disparity_corr
));
}
}
/*
{ int indx = 0;
{ int indx = 0;
for (int i =0; i < corr_size-1; i++){
for (int i =0; i < corr_size-1; i++){
for (int j = i+1; j < corr_size; j++){
for (int j = i+1; j < corr_size; j++){
...
@@ -204,7 +207,7 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -204,7 +207,7 @@ public class ImageDtt extends ImageDttCPU {
}
}
}
}
}
}
*/
//// final int first_color = isMonochrome()? MONO_CHN : 0; // color that is non-zero
//// final int first_color = isMonochrome()? MONO_CHN : 0; // color that is non-zero
...
@@ -316,8 +319,8 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -316,8 +319,8 @@ public class ImageDtt extends ImageDttCPU {
DttRad2
dtt
=
new
DttRad2
(
transform_size
);
DttRad2
dtt
=
new
DttRad2
(
transform_size
);
dtt
.
set_window
(
window_type
);
dtt
.
set_window
(
window_type
);
final
double
[]
lt_window
=
dtt
.
getWin2d
();
// [256]
final
double
[]
lt_window
=
dtt
.
getWin2d
();
// [256]
- never used
final
double
[]
lt_window2
=
new
double
[
lt_window
.
length
];
// squared
final
double
[]
lt_window2
=
new
double
[
lt_window
.
length
];
// squared
- never used
for
(
int
i
=
0
;
i
<
lt_window
.
length
;
i
++)
lt_window2
[
i
]
=
lt_window
[
i
]
*
lt_window
[
i
];
for
(
int
i
=
0
;
i
<
lt_window
.
length
;
i
++)
lt_window2
[
i
]
=
lt_window
[
i
]
*
lt_window
[
i
];
...
@@ -356,20 +359,20 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -356,20 +359,20 @@ public class ImageDtt extends ImageDttCPU {
};
};
gpuQuad
.
setLpfRbg
(
// constants memory - same for all cameras
gpuQuad
.
setLpfRbg
(
// constants memory - same for all cameras
lpf_rgb
,
lpf_rgb
,
false
);
// !batch_mode
);
globalDebugLevel
>
-
1
);
final
float
[]
lpf_flat
=
floatGetCltLpfFd
(
gpu_sigma_corr
);
final
float
[]
lpf_flat
=
floatGetCltLpfFd
(
gpu_sigma_corr
);
gpuQuad
.
setLpfCorr
(
// constants memory - same for all cameras
gpuQuad
.
setLpfCorr
(
// constants memory - same for all cameras
"lpf_corr"
,
// String const_name, // "lpf_corr"
"lpf_corr"
,
// String const_name, // "lpf_corr"
lpf_flat
,
lpf_flat
,
false
);
// !batch_mode
);
globalDebugLevel
>
-
1
);
final
float
[]
lpf_rb_flat
=
floatGetCltLpfFd
(
gpu_sigma_rb_corr
);
final
float
[]
lpf_rb_flat
=
floatGetCltLpfFd
(
gpu_sigma_rb_corr
);
gpuQuad
.
setLpfCorr
(
// constants memory - same for all cameras
gpuQuad
.
setLpfCorr
(
// constants memory - same for all cameras
"lpf_rb_corr"
,
// String const_name, // "lpf_corr"
"lpf_rb_corr"
,
// String const_name, // "lpf_corr"
lpf_rb_flat
,
lpf_rb_flat
,
false
);
// !batch_mode
);
globalDebugLevel
>
-
1
);
gpuQuad
.
setTasks
(
// copy tp_tasks to the GPU memory
gpuQuad
.
setTasks
(
// copy tp_tasks to the GPU memory
tp_tasks
,
// TpTask [] tile_tasks,
tp_tasks
,
// TpTask [] tile_tasks,
...
@@ -382,7 +385,7 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -382,7 +385,7 @@ public class ImageDtt extends ImageDttCPU {
for
(
int
ncam
=
0
;
ncam
<
iclt_fimg
.
length
;
ncam
++)
{
for
(
int
ncam
=
0
;
ncam
<
iclt_fimg
.
length
;
ncam
++)
{
iclt_fimg
[
ncam
]
=
gpuQuad
.
getRBG
(
ncam
);
// retrieve data from GPU
iclt_fimg
[
ncam
]
=
gpuQuad
.
getRBG
(
ncam
);
// retrieve data from GPU
}
}
}
}
else
{
gpuQuad
.
execImcltRbgAll
(
isMonochrome
());}
// just for testing
// does it need texture tiles to be output?
// does it need texture tiles to be output?
if
(
texture_img
!=
null
)
{
if
(
texture_img
!=
null
)
{
Rectangle
woi
=
new
Rectangle
();
// will be filled out to match actual available image
Rectangle
woi
=
new
Rectangle
();
// will be filled out to match actual available image
...
@@ -443,7 +446,7 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -443,7 +446,7 @@ public class ImageDtt extends ImageDttCPU {
if
(
fneed_corr
)
{
if
(
fneed_corr
)
{
//Generate 2D phase correlations from the CLT representation
//Generate 2D phase correlations from the CLT representation
gpuQuad
.
execCorr2D
(
gpuQuad
.
execCorr2D
(
scales
,
// double [] scales,
col_weights
,
//
scales,// double [] scales,
gpu_fat_zero
,
// double fat_zero);
gpu_fat_zero
,
// double fat_zero);
gpu_corr_rad
);
// int corr_radius
gpu_corr_rad
);
// int corr_radius
//Show 2D correlations
//Show 2D correlations
...
@@ -452,6 +455,24 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -452,6 +455,24 @@ public class ImageDtt extends ImageDttCPU {
final
float
[][]
fcorr2D
=
gpuQuad
.
getCorr2D
(
final
float
[][]
fcorr2D
=
gpuQuad
.
getCorr2D
(
gpu_corr_rad
);
// int corr_rad);
gpu_corr_rad
);
// int corr_rad);
if
(
corr_indices
.
length
>
0
)
{
if
(
corr_indices
.
length
>
0
)
{
if
(
true
)
{
int
[]
wh
=
new
int
[
2
];
double
[][]
dbg_corr
=
GPUTileProcessor
.
getCorr2DView
(
tilesX
,
tilesY
,
corr_indices
,
fcorr2D
,
wh
);
(
new
ShowDoubleFloatArrays
()).
showArrays
(
dbg_corr
,
wh
[
0
],
wh
[
1
],
true
,
"dbg-corr2D"
,
// name+"-CORR2D-D"+clt_parameters.disparity,
GPUTileProcessor
.
getCorrTitles
());
}
final
int
corr_length
=
fcorr2D
[
0
].
length
;
// all correlation tiles have the same size
final
int
corr_length
=
fcorr2D
[
0
].
length
;
// all correlation tiles have the same size
// assuming that the correlation pairs sets are the same for each tile that has correlations
// assuming that the correlation pairs sets are the same for each tile that has correlations
// find this number
// find this number
...
@@ -464,62 +485,13 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -464,62 +485,13 @@ public class ImageDtt extends ImageDttCPU {
final
int
num_tiles
=
corr_indices
.
length
/
num_tile_corr
;
final
int
num_tiles
=
corr_indices
.
length
/
num_tile_corr
;
/*
// convert to 6-layer image using tasks
double [][] dbg_corr = GPUTileProcessor.getCorr2DView(
tilesX,
tilesY,
corr_indices,
corr2D,
wh);
(new ShowDoubleFloatArrays()).showArrays(
dbg_corr,
wh[0],
wh[1],
true,
name+"-CORR2D-D"+clt_parameters.disparity,
GPUTileProcessor.getCorrTitles());
*/
for
(
int
ithread
=
0
;
ithread
<
threads
.
length
;
ithread
++)
{
for
(
int
ithread
=
0
;
ithread
<
threads
.
length
;
ithread
++)
{
threads
[
ithread
]
=
new
Thread
()
{
threads
[
ithread
]
=
new
Thread
()
{
@Override
@Override
public
void
run
()
{
public
void
run
()
{
// int tileY,tileX,tIndex;
// int tileY,tileX,tIndex;
double
[][]
corrs
=
new
double
[
GPUTileProcessor
.
NUM_PAIRS
][
corr_length
];
// 225-long (15x15)
//
double [][] corrs = new double [GPUTileProcessor.NUM_PAIRS][corr_length]; // 225-long (15x15)
/*
DttRad2 dtt = new DttRad2(transform_size);
dtt.set_window(window_type);
int tileY,tileX,tIndex; // , chn;
// showDoubleFloatArrays sdfa_instance = new showDoubleFloatArrays(); // just for debugging?
double centerX; // center of aberration-corrected (common model) tile, X
double centerY; //
double [][] fract_shiftsXY = new double[quad][];
double [][] tcorr_combo = null; // [15*15] pixel space
double [][][] tcorr_partial = null; // [quad][numcol+1][15*15]
double [][][][] tcorr_tpartial = null; // [quad][numcol+1][4][8*8]
double [] ports_rgb = null;
double [][] rXY;
if (use_main) {
rXY = geometryCorrection.getRXY(true); // boolean use_rig_offsets,
} else {
rXY = geometryCorrection.getRXY(false); // boolean use_rig_offsets,
}
Correlation2d corr2d = new Correlation2d(
imgdtt_params, // ImageDttParameters imgdtt_params,
transform_size, // int transform_size,
2.0, // double wndx_scale, // (wndy scale is always 1.0)
isMonochrome(), // boolean monochrome,
(globalDebugLevel > -1)); // boolean debug)
corr2d.createOrtoNotch(
imgdtt_params.getEnhOrthoWidth(isAux()), // double getEnhOrthoWidth(isAux()),
imgdtt_params.getEnhOrthoScale(isAux()), //double getEnhOrthoScale(isAux()),
(imgdtt_params.lma_debug_level > 1)); // boolean debug);
*/
Correlation2d
corr2d
=
new
Correlation2d
(
Correlation2d
corr2d
=
new
Correlation2d
(
imgdtt_params
,
// ImageDttParameters imgdtt_params,
imgdtt_params
,
// ImageDttParameters imgdtt_params,
transform_size
,
// int transform_size,
transform_size
,
// int transform_size,
...
@@ -532,6 +504,7 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -532,6 +504,7 @@ public class ImageDtt extends ImageDttCPU {
(
imgdtt_params
.
lma_debug_level
>
1
));
// boolean debug);
(
imgdtt_params
.
lma_debug_level
>
1
));
// boolean debug);
for
(
int
indx_tile
=
ai
.
getAndIncrement
();
indx_tile
<
num_tiles
;
indx_tile
=
ai
.
getAndIncrement
())
{
for
(
int
indx_tile
=
ai
.
getAndIncrement
();
indx_tile
<
num_tiles
;
indx_tile
=
ai
.
getAndIncrement
())
{
double
[][]
corrs
=
new
double
[
GPUTileProcessor
.
NUM_PAIRS
][
corr_length
];
// 225-long (15x15)
int
indx_corr
=
indx_tile
*
num_tile_corr
;
int
indx_corr
=
indx_tile
*
num_tile_corr
;
int
nt
=
(
corr_indices
[
indx_corr
]
>>
GPUTileProcessor
.
CORR_NTILE_SHIFT
);
int
nt
=
(
corr_indices
[
indx_corr
]
>>
GPUTileProcessor
.
CORR_NTILE_SHIFT
);
int
tileX
=
nt
%
tilesX
;
int
tileX
=
nt
%
tilesX
;
...
@@ -543,7 +516,7 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -543,7 +516,7 @@ public class ImageDtt extends ImageDttCPU {
assert
pair
<
GPUTileProcessor
.
NUM_PAIRS
:
"invalid correllation pair"
;
assert
pair
<
GPUTileProcessor
.
NUM_PAIRS
:
"invalid correllation pair"
;
pair_mask
|=
(
1
<<
pair
);
pair_mask
|=
(
1
<<
pair
);
for
(
int
i
=
0
;
i
<
corr_length
;
i
++)
{
for
(
int
i
=
0
;
i
<
corr_length
;
i
++)
{
corrs
[
pair
][
i
]
=
fcorr2D
[
indx_corr
][
i
];
// from float d
o double
corrs
[
pair
][
i
]
=
gpu_corr_scale
*
fcorr2D
[
indx_corr
][
i
];
// from float t
o double
}
}
indx_corr
++;
indx_corr
++;
}
}
...
@@ -934,6 +907,14 @@ public class ImageDtt extends ImageDttCPU {
...
@@ -934,6 +907,14 @@ public class ImageDtt extends ImageDttCPU {
}
}
}
}
}
}
}
else
{
// if (lma != null)
if
(
imgdtt_params
.
corr_poly_only
)
{
// discard tile if LMA failed
disp_str
[
0
]
=
Double
.
NaN
;
disp_str
[
1
]
=
0.0
;
disparity_map
[
DISPARITY_INDEX_CM
]
[
tIndex
]
=
disp_str
[
0
];
disparity_map
[
DISPARITY_STRENGTH_INDEX
]
[
tIndex
]
=
disp_str
[
1
];
}
}
}
}
}
}
// end of if (corr_stat != null)
}
// end of if (corr_stat != null)
...
...
src/main/java/com/elphel/imagej/tileprocessor/ImageDttCPU.java
View file @
2eae496d
...
@@ -2439,7 +2439,9 @@ public class ImageDttCPU {
...
@@ -2439,7 +2439,9 @@ public class ImageDttCPU {
for
(
int
i
=
0
;
i
<
debug_offsets
.
length
;
i
++)
for
(
int
j
=
0
;
j
<
debug_offsets
[
i
].
length
;
j
++)
{
for
(
int
i
=
0
;
i
<
debug_offsets
.
length
;
i
++)
for
(
int
j
=
0
;
j
<
debug_offsets
[
i
].
length
;
j
++)
{
debug_offsets
[
i
][
j
]
=
imgdtt_params
.
lma_dbg_offset
[
i
][
j
]*
imgdtt_params
.
lma_dbg_scale
;
debug_offsets
[
i
][
j
]
=
imgdtt_params
.
lma_dbg_offset
[
i
][
j
]*
imgdtt_params
.
lma_dbg_scale
;
}
}
final
double
[]
dbg_corr_shift
=((
imgdtt_params
.
pcorr_dbg_offsx
!=
0.0
)
||
(
imgdtt_params
.
pcorr_dbg_offsy
!=
0.0
))?
(
new
double
[]
{
imgdtt_params
.
pcorr_dbg_offsx
,
imgdtt_params
.
pcorr_dbg_offsy
}):
null
;
final
boolean
macro_mode
=
macro_scale
!=
1
;
// correlate tile data instead of the pixel data
final
boolean
macro_mode
=
macro_scale
!=
1
;
// correlate tile data instead of the pixel data
final
int
quad
=
4
;
// number of subcameras
final
int
quad
=
4
;
// number of subcameras
...
@@ -2566,7 +2568,11 @@ public class ImageDttCPU {
...
@@ -2566,7 +2568,11 @@ public class ImageDttCPU {
{
0.5
,
0.5
}};
{
0.5
,
0.5
}};
final
int
transform_len
=
transform_size
*
transform_size
;
final
int
transform_len
=
transform_size
*
transform_size
;
final
double
[]
filter
=
doubleGetCltLpfFd
(
corr_sigma
);
final
double
[]
filter
=
doubleGetCltLpfFd
(
corr_sigma
);
final
double
[]
filter_rb
=
doubleGetCltLpfFd
(
imgdtt_params
.
pcorr_sigma_rb
);
final
double
[]
filter_common
=
doubleGetCltLpfFd
(
imgdtt_params
.
getCorrSigma
(
isMonochrome
()));
final
double
pcorr_fat_zero
=
imgdtt_params
.
getFatZero
(
isMonochrome
());
// prepare disparity maps and weights
// prepare disparity maps and weights
final
int
max_search_radius
=
(
int
)
Math
.
abs
(
max_corr_radius
);
// use negative max_corr_radius for squares instead of circles?
final
int
max_search_radius
=
(
int
)
Math
.
abs
(
max_corr_radius
);
// use negative max_corr_radius for squares instead of circles?
...
@@ -2672,7 +2678,7 @@ public class ImageDttCPU {
...
@@ -2672,7 +2678,7 @@ public class ImageDttCPU {
}
}
}
}
boolean
debugTile
=(
tileX
==
debug_tileX
)
&&
(
tileY
==
debug_tileY
)
&&
(
globalDebugLevel
>
-
1
);
boolean
debugTile
=(
tileX
==
debug_tileX
)
&&
(
tileY
==
debug_tileY
)
&&
(
globalDebugLevel
>
-
1
);
boolean
debugTile0
=(
tileX
==
debug_tileX
)
&&
(
tileY
==
debug_tileY
)
&&
(
globalDebugLevel
>
-
3
);
final
int
[]
overexp_all
=
(
saturation_imp
!=
null
)
?
(
new
int
[
2
]):
null
;
final
int
[]
overexp_all
=
(
saturation_imp
!=
null
)
?
(
new
int
[
2
]):
null
;
// Moved from inside chn loop
// Moved from inside chn loop
...
@@ -3054,7 +3060,60 @@ public class ImageDttCPU {
...
@@ -3054,7 +3060,60 @@ public class ImageDttCPU {
// Debug feature - only calculated if requested
// Debug feature - only calculated if requested
// if ((clt_corr_partial != null) && imgdtt_params.corr_mode_debug) {
// if ((clt_corr_partial != null) && imgdtt_params.corr_mode_debug) {
if
((
clt_corr_partial
!=
null
)
&&
(
imgdtt_params
.
corr_mode_debug
||
imgdtt_params
.
gpu_mode_debug
))
{
if
((
clt_corr_partial
!=
null
)
&&
(
imgdtt_params
.
corr_mode_debug
||
imgdtt_params
.
gpu_mode_debug
))
{
if
(
debugTile0
)
{
System
.
out
.
println
(
"Debugging tileY = "
+
tileY
+
" tileX = "
+
tileX
);
System
.
out
.
println
(
"Debugging tileY = "
+
tileY
+
" tileX = "
+
tileX
);
System
.
out
.
println
(
"Debugging tileY = "
+
tileY
+
" tileX = "
+
tileX
);
}
double
[][][]
corr_pairs_td
=
corr2d
.
correlateCompositeTD
(
clt_data
,
// double [][][][][][] clt_data,
tileX
,
// int tileX,
tileY
,
// int tileY,
0x3f
,
// int pairs_mask,
filter_rb
,
// double [] lpf_rb, // extra lpf for red and blue (unused for mono) or null
getScaleStrengths
(),
// double scale_value, // scale correlation value
col_weights
);
// double [] col_weights);
double
[][]
corrs_ortho_td
=
corr2d
.
combineCorrelationsTD
(
corr_pairs_td
,
// double [][][] corr_combo_td, // vertical will be transposed, other diagonal flipped vertically (should be separate)
0x0f
);
// int pairs_mask); // vertical
if
(
dbg_corr_shift
!=
null
)
{
fract_shift
(
// fractional shift in transform domain. Currently uses sin/cos - change to tables with 2? rotations // USED in lwir
corrs_ortho_td
,
// double [][] clt_tile,
dbg_corr_shift
[
0
],
// double shiftX,
dbg_corr_shift
[
1
],
// double shiftY,
false
);
// boolean bdebug);
}
corr2d
.
normalize_TD
(
corrs_ortho_td
,
// double [][] td,
filter_common
,
// double [] lpf, // or null
pcorr_fat_zero
);
// double fat_zero);
double
[]
corrs_ortho
=
corr2d
.
convertCorrToPD
(
corrs_ortho_td
);
// double [][] td);
double
[][]
corrs_cross_td
=
corr2d
.
combineCorrelationsTD
(
corr_pairs_td
,
// double [][][] corr_combo_td, // vertical will be transposed, other diagonal flipped vertically (should be separate)
0x30
);
// int pairs_mask); // vertical
if
(
dbg_corr_shift
!=
null
)
{
fract_shift
(
// fractional shift in transform domain. Currently uses sin/cos - change to tables with 2? rotations // USED in lwir
corrs_cross_td
,
// double [][] clt_tile,
dbg_corr_shift
[
0
],
// double shiftX,
dbg_corr_shift
[
1
],
// double shiftY,
false
);
// boolean bdebug);
}
corr2d
.
normalize_TD
(
corrs_cross_td
,
// double [][] td,
filter_common
,
// double [] lpf, // or null
pcorr_fat_zero
);
// double fat_zero);
double
[]
corrs_cross
=
corr2d
.
convertCorrToPD
(
corrs_cross_td
);
// double [][] td);
double
[]
strip_ortho
=
corr2d
.
combineInterpolatedCorrelations
(
double
[]
strip_ortho
=
corr2d
.
combineInterpolatedCorrelations
(
strips
,
// double [][] strips,
strips
,
// double [][] strips,
0x0f
,
// int pairs_mask,
0x0f
,
// int pairs_mask,
...
@@ -3093,8 +3152,10 @@ public class ImageDttCPU {
...
@@ -3093,8 +3152,10 @@ public class ImageDttCPU {
clt_corr_partial
[
tileY
][
tileX
][
0
][
3
]
=
corrs
[
3
];
// 4
clt_corr_partial
[
tileY
][
tileX
][
0
][
3
]
=
corrs
[
3
];
// 4
clt_corr_partial
[
tileY
][
tileX
][
1
][
0
]
=
corrs
[
4
];
// 5
clt_corr_partial
[
tileY
][
tileX
][
1
][
0
]
=
corrs
[
4
];
// 5
clt_corr_partial
[
tileY
][
tileX
][
1
][
1
]
=
corrs
[
5
];
// 6
clt_corr_partial
[
tileY
][
tileX
][
1
][
1
]
=
corrs
[
5
];
// 6
clt_corr_partial
[
tileY
][
tileX
][
1
][
2
]
=
corr2d
.
debugStrip
(
strip_hor
);
// 7
clt_corr_partial
[
tileY
][
tileX
][
1
][
2
]
=
corrs_ortho
;
// 7
clt_corr_partial
[
tileY
][
tileX
][
1
][
3
]
=
corr2d
.
debugStrip
(
strip_vert
);
// 8
clt_corr_partial
[
tileY
][
tileX
][
1
][
3
]
=
corrs_cross
;
// 8
// clt_corr_partial[tileY][tileX][1][2] = corr2d.debugStrip(strip_hor); // 7
// clt_corr_partial[tileY][tileX][1][3] = corr2d.debugStrip(strip_vert); // 8
clt_corr_partial
[
tileY
][
tileX
][
2
][
0
]
=
corr2d
.
debugStrip
(
strips
[
4
]);
// 9
clt_corr_partial
[
tileY
][
tileX
][
2
][
0
]
=
corr2d
.
debugStrip
(
strips
[
4
]);
// 9
clt_corr_partial
[
tileY
][
tileX
][
2
][
1
]
=
corr2d
.
debugStrip
(
strips
[
5
]);
// 10
clt_corr_partial
[
tileY
][
tileX
][
2
][
1
]
=
corr2d
.
debugStrip
(
strips
[
5
]);
// 10
clt_corr_partial
[
tileY
][
tileX
][
2
][
2
]
=
corr2d
.
debugStrip2
(
strip_hor
);
// 11
clt_corr_partial
[
tileY
][
tileX
][
2
][
2
]
=
corr2d
.
debugStrip2
(
strip_hor
);
// 11
...
...
src/main/java/com/elphel/imagej/tileprocessor/ImageDttParameters.java
View file @
2eae496d
This diff is collapsed.
Click to expand it.
src/main/java/com/elphel/imagej/tileprocessor/QuadCLT.java
View file @
2eae496d
...
@@ -277,7 +277,7 @@ public class QuadCLT extends QuadCLTCPU {
...
@@ -277,7 +277,7 @@ public class QuadCLT extends QuadCLTCPU {
};
};
gpuQuad
.
setLpfRbg
(
// constants memory - same for all cameras
gpuQuad
.
setLpfRbg
(
// constants memory - same for all cameras
lpf_rgb
,
lpf_rgb
,
!
batch_mode
);
true
);
//
!batch_mode);
float
[]
lpf_flat
=
image_dtt
.
floatGetCltLpfFd
(
clt_parameters
.
getGpuCorrSigma
(
is_mono
));
float
[]
lpf_flat
=
image_dtt
.
floatGetCltLpfFd
(
clt_parameters
.
getGpuCorrSigma
(
is_mono
));
...
@@ -499,10 +499,20 @@ public class QuadCLT extends QuadCLTCPU {
...
@@ -499,10 +499,20 @@ public class QuadCLT extends QuadCLTCPU {
boolean
is_lwir
=
quadCLT_main
.
isLwir
();
boolean
is_lwir
=
quadCLT_main
.
isLwir
();
double
fat_zero
=
clt_parameters
.
getGpuFatZero
(
is_mono
);
// 30.0;
double
fat_zero
=
clt_parameters
.
getGpuFatZero
(
is_mono
);
// 30.0;
/*
double [] scales = (is_mono) ? (new double [] {1.0}) :(new double [] {
double [] scales = (is_mono) ? (new double [] {1.0}) :(new double [] {
clt_parameters.gpu_weight_r, // 0.25
clt_parameters.gpu_weight_r, // 0.25
clt_parameters.gpu_weight_b, // 0.25
clt_parameters.gpu_weight_b, // 0.25
1.0 - clt_parameters.gpu_weight_r - clt_parameters.gpu_weight_b}); // 0.5
1.0 - clt_parameters.gpu_weight_r - clt_parameters.gpu_weight_b}); // 0.5
*/
double
corr_green
=
1.0
/(
1.0
+
clt_parameters
.
gpu_weight_r
+
clt_parameters
.
gpu_weight_b
);
// green color
// the same, as col_weights, for separate debugging
double
[]
scales
=
(
is_mono
)
?
(
new
double
[]
{
1.0
})
:(
new
double
[]
{
clt_parameters
.
gpu_weight_r
*
corr_green
,
clt_parameters
.
gpu_weight_b
*
corr_green
,
corr_green
});
double
cwgreen
=
1.0
/(
1.0
+
clt_parameters
.
corr_red
+
clt_parameters
.
corr_blue
);
// green color
double
cwgreen
=
1.0
/(
1.0
+
clt_parameters
.
corr_red
+
clt_parameters
.
corr_blue
);
// green color
double
[]
col_weights
=
(
is_mono
)
?
(
new
double
[]
{
1.0
})
:(
new
double
[]
{
double
[]
col_weights
=
(
is_mono
)
?
(
new
double
[]
{
1.0
})
:(
new
double
[]
{
clt_parameters
.
corr_red
*
cwgreen
,
clt_parameters
.
corr_red
*
cwgreen
,
...
@@ -522,20 +532,20 @@ public class QuadCLT extends QuadCLTCPU {
...
@@ -522,20 +532,20 @@ public class QuadCLT extends QuadCLTCPU {
};
};
quadCLT_main
.
getGPU
().
setLpfRbg
(
// constants memory - same for all cameras
quadCLT_main
.
getGPU
().
setLpfRbg
(
// constants memory - same for all cameras
lpf_rgb
,
lpf_rgb
,
debugLevel
>
-
1
);
debugLevel
>
-
1
);
// -3
float
[]
lpf_flat
=
image_dtt
.
floatGetCltLpfFd
(
clt_parameters
.
getGpuCorrSigma
(
is_mono
));
float
[]
lpf_flat
=
image_dtt
.
floatGetCltLpfFd
(
clt_parameters
.
getGpuCorrSigma
(
is_mono
));
quadCLT_main
.
getGPU
().
setLpfCorr
(
// constants memory - same for all cameras
quadCLT_main
.
getGPU
().
setLpfCorr
(
// constants memory - same for all cameras
"lpf_corr"
,
// String const_name, // "lpf_corr"
"lpf_corr"
,
// String const_name, // "lpf_corr"
lpf_flat
,
lpf_flat
,
debugLevel
>
-
1
);
debugLevel
>
-
1
);
// -3
float
[]
lpf_rb_flat
=
image_dtt
.
floatGetCltLpfFd
(
clt_parameters
.
getGpuCorrRBSigma
(
is_mono
));
float
[]
lpf_rb_flat
=
image_dtt
.
floatGetCltLpfFd
(
clt_parameters
.
getGpuCorrRBSigma
(
is_mono
));
quadCLT_main
.
getGPU
().
setLpfCorr
(
// constants memory - same for all cameras
quadCLT_main
.
getGPU
().
setLpfCorr
(
// constants memory - same for all cameras
"lpf_rb_corr"
,
// String const_name, // "lpf_corr"
"lpf_rb_corr"
,
// String const_name, // "lpf_corr"
lpf_rb_flat
,
lpf_rb_flat
,
debugLevel
>
-
1
);
debugLevel
>
-
1
);
// -3
final
boolean
use_aux
=
false
;
// currently GPU is configured for a single quad camera
final
boolean
use_aux
=
false
;
// currently GPU is configured for a single quad camera
...
@@ -1171,7 +1181,9 @@ public class QuadCLT extends QuadCLTCPU {
...
@@ -1171,7 +1181,9 @@ public class QuadCLT extends QuadCLTCPU {
texture_woi
,
// texture_woi, // null or generated texture location/size
texture_woi
,
// texture_woi, // null or generated texture location/size
null
,
// iclt_fimg, // will return quad images or null to skip, use quadCLT.linearStackToColor
null
,
// iclt_fimg, // will return quad images or null to skip, use quadCLT.linearStackToColor
// new parameters, will replace some other?
// new parameters, will replace some other?
clt_parameters
.
getFatZero
(
isMonochrome
()),
// final double gpu_fat_zero, // clt_parameters.getGpuFatZero(is_mono);absolute == 30.0
// clt_parameters.getFatZero(isMonochrome()), // final double gpu_fat_zero, // clt_parameters.getGpuFatZero(is_mono);absolute == 30.0\
clt_parameters
.
gpu_corr_scale
,
// gpu_corr_scale, // 0.75; // reduce GPU-generated correlation values
clt_parameters
.
getGpuFatZero
(
isMonochrome
()),
// final double gpu_fat_zero, // clt_parameters.getGpuFatZero(is_mono);absolute == 30.0\
clt_parameters
.
gpu_sigma_r
,
// 0.9, 1.1
clt_parameters
.
gpu_sigma_r
,
// 0.9, 1.1
clt_parameters
.
gpu_sigma_b
,
// 0.9, 1.1
clt_parameters
.
gpu_sigma_b
,
// 0.9, 1.1
clt_parameters
.
gpu_sigma_g
,
// 0.6, 0.7
clt_parameters
.
gpu_sigma_g
,
// 0.6, 0.7
...
...
src/main/java/com/elphel/imagej/tileprocessor/QuadCLTCPU.java
View file @
2eae496d
...
@@ -9954,7 +9954,7 @@ public class QuadCLTCPU {
...
@@ -9954,7 +9954,7 @@ public class QuadCLTCPU {
tilesX
*(
2
*
image_dtt
.
transform_size
),
tilesX
*(
2
*
image_dtt
.
transform_size
),
tilesY
*(
2
*
image_dtt
.
transform_size
),
tilesY
*(
2
*
image_dtt
.
transform_size
),
true
,
true
,
image_name
+
sAux
()+
"-PART_CORR-
G
PU-D"
+
clt_parameters
.
disparity
);
image_name
+
sAux
()+
"-PART_CORR-
C
PU-D"
+
clt_parameters
.
disparity
);
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment