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
f96b0aec
Commit
f96b0aec
authored
Jan 06, 2017
by
Andrey Filippov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
trying with pure anti-periodic kernels
parent
6520b841
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
305 additions
and
84 deletions
+305
-84
EyesisDCT.java
src/main/java/EyesisDCT.java
+98
-16
Eyesis_Correction.java
src/main/java/Eyesis_Correction.java
+88
-29
FactorConvKernel.java
src/main/java/FactorConvKernel.java
+119
-39
No files found.
src/main/java/EyesisDCT.java
View file @
f96b0aec
...
...
@@ -151,7 +151,13 @@ public class EyesisDCT {
final
AtomicInteger
ai
=
new
AtomicInteger
(
0
);
final
int
numberOfKernels
=
kernelNumHor
*
kernelNumVert
*
nChn
;
final
int
numberOfKernelsInChn
=
kernelNumHor
*
kernelNumVert
;
final
int
dct_size
=
dct_parameters
.
dct_size
;
final
int
preTargetSize
=
4
*
dct_size
;
final
int
targetSize
=
2
*
dct_size
;
// normally 16
final
double
[]
anitperiodic_window
=
createAntiperiodicWindow
(
dct_size
);
final
long
startTime
=
System
.
nanoTime
();
System
.
out
.
println
(
"calculateDCTKernel():numberOfKernels="
+
numberOfKernels
);
for
(
int
ithread
=
0
;
ithread
<
threads
.
length
;
ithread
++)
{
threads
[
ithread
]
=
new
Thread
()
{
...
...
@@ -160,11 +166,16 @@ public class EyesisDCT {
if
(
dct_parameters
.
decimateSigma
>
0
)
gb
=
new
DoubleGaussianBlur
();
float
[]
kernelPixels
=
null
;
// will be initialized at first use NOT yet?
double
[]
kernel
=
new
double
[
kernelSize
*
kernelSize
];
int
targetSize
=
dct_parameters
.
asym_size
+
2
*
dct_parameters
.
dct_size
-
2
;
double
[]
target_kernel
=
new
double
[
targetSize
*
targetSize
];
// int targetSize = dct_parameters.asym_size + 2 * dct_parameters.dct_size - 2;
double
[]
pre_target_kernel
=
new
double
[
preTargetSize
*
preTargetSize
];
// before made antiperiodic
double
[]
target_kernel
=
new
double
[
targetSize
*
targetSize
];
// strictly antiperiodic in both x and y directions
FactorConvKernel
factorConvKernel
=
new
FactorConvKernel
();
factorConvKernel
.
setDebugLevel
(
0
);
// globalDebugLevel);
factorConvKernel
.
setTargetWindowMode
(
dct_parameters
.
dbg_window_mode
,
dct_parameters
.
centerWindowToTarget
);
// factorConvKernel.setTargetWindowMode (dct_parameters.dbg_window_mode, dct_parameters.centerWindowToTarget);
factorConvKernel
.
setTargetWindowMode
(
dct_parameters
.
centerWindowToTarget
);
factorConvKernel
.
numIterations
=
dct_parameters
.
LMA_steps
;
factorConvKernel
.
setAsymCompactness
(
dct_parameters
.
compactness
,
dct_parameters
.
asym_tax_free
);
factorConvKernel
.
setSymCompactness
(
dct_parameters
.
sym_compactness
);
...
...
@@ -194,33 +205,40 @@ public class EyesisDCT {
if
((
dct_parameters
.
decimation
==
2
)
&&
(
dct_parameters
.
decimateSigma
<
0
))
{
reformatKernel2
(
// averages by exactly 2 (decimate==2)
kernel
,
// will be blured in-place
target_kernel
,
// expand/crop, blur/decimate result
kernel
,
pre_target_kernel
,
// expand/crop, blur/decimate result (32x32)
kernelSize
,
targetSize
);
preTargetSize
);
// 32
}
else
{
reformatKernel
(
kernel
,
// will be blured in-place
target_kernel
,
// expand/crop, blur/decimate result
kernel
,
// will be blur
r
ed in-place
pre_target_kernel
,
// expand/crop, blur/decimate result (32x32)
kernelSize
,
targetSize
,
preTargetSize
,
// 32
dct_parameters
.
decimation
,
dct_parameters
.
decimateSigma
,
gb
);
}
if
(
dct_parameters
.
normalize
)
{
if
(
dct_parameters
.
normalize
)
{
// or should it be normalized after antiperiodic?
double
s
=
0.0
;
for
(
int
i
=
0
;
i
<
target_kernel
.
length
;
i
++){
s
+=
target_kernel
[
i
];
for
(
int
i
=
0
;
i
<
pre_
target_kernel
.
length
;
i
++){
s
+=
pre_
target_kernel
[
i
];
}
for
(
int
i
=
0
;
i
<
target_kernel
.
length
;
i
++){
target_kernel
[
i
]/=
s
;
for
(
int
i
=
0
;
i
<
pre_
target_kernel
.
length
;
i
++){
pre_
target_kernel
[
i
]/=
s
;
}
if
(
globalDebugLevel
>
1
){
// was already close to 1.0
System
.
out
.
println
(
tileX
+
"/"
+
tileY
+
" s="
+
s
);
}
}
// int numAsym =
// make exactly anitperiodic
makeAntiperiodic
(
dct_size
,
pre_target_kernel
,
// 16*dct_zize*dct_zize
anitperiodic_window
,
// 16*dct_zize*dct_zize
target_kernel
);
// 4*dct_zize*dct_zize
factorConvKernel
.
calcKernels
(
target_kernel
,
dct_parameters
.
asym_size
,
...
...
@@ -439,7 +457,7 @@ public class EyesisDCT {
public
double
[]
reformatKernel
(
double
[]
src_kernel
,
// will be blured in-place
int
src_size
,
// typical 64
int
dst_size
,
// typical 15
int
dst_size
,
// typical 15
// destination size
int
decimation
,
// typical 2
double
sigma
)
{
...
...
@@ -534,6 +552,70 @@ public class EyesisDCT {
}
}
private
double
[]
createAntiperiodicWindow
(
int
dct_size
)
{
double
[]
wnd
=
new
double
[
4
*
dct_size
];
for
(
int
i
=
0
;
i
<
dct_size
;
i
++){
wnd
[
i
]=
0.5
*
(
1.0
-
Math
.
cos
(
Math
.
PI
*
i
/
dct_size
));
wnd
[
i
+
1
*
dct_size
]
=
1.0
;
wnd
[
i
+
2
*
dct_size
]
=
1.0
;
wnd
[
i
+
3
*
dct_size
]
=
1.0
-
wnd
[
i
];
}
int
n4
=
dct_size
*
4
;
double
[]
window
=
new
double
[
n4
*
n4
];
for
(
int
i
=
0
;
i
<
n4
;
i
++){
for
(
int
j
=
0
;
j
<
n4
;
j
++){
window
[
i
*
n4
+
j
]
=
wnd
[
i
]*
wnd
[
j
];
}
}
return
window
;
}
public
double
[]
makeAntiperiodic
(
int
dct_size
,
double
[]
src_kernel
)
// 16*dct_zize*dct_zize
{
double
[]
window
=
createAntiperiodicWindow
(
dct_size
);
double
[]
antiperiodic_kernel
=
new
double
[
4
*
dct_size
*
dct_size
];
makeAntiperiodic
(
dct_size
,
src_kernel
,
// 16*dct_zize*dct_zize
window
,
// 16*dct_zize*dct_zize
antiperiodic_kernel
);
// 4*dct_zize*dct_zize
return
antiperiodic_kernel
;
}
private
void
makeAntiperiodic
(
int
dct_size
,
double
[]
src_kernel
,
// 16*dct_zize*dct_zize
double
[]
window
,
// 16*dct_zize*dct_zize
double
[]
antiperiodic_kernel
)
// 4*dct_zize*dct_zize
{
int
n2
=
dct_size
*
2
;
int
n4
=
dct_size
*
4
;
for
(
int
i
=
0
;
i
<
n2
;
i
++){
for
(
int
j
=
0
;
j
<
n2
;
j
++){
int
dst_index
=
i
*
n2
+
j
;
int
isrc
=
i
+
dct_size
;
int
jsrc
=
j
+
dct_size
;
int
isrcp
=
(
isrc
+
n2
)
%
n4
;
int
jsrcp
=
(
jsrc
+
n2
)
%
n4
;
int
src_index0
=
(
isrc
)
*
n4
+
jsrc
;
int
src_index1
=
(
isrcp
)
*
n4
+
jsrc
;
int
src_index2
=
(
isrc
)
*
n4
+
jsrcp
;
int
src_index3
=
(
isrcp
)
*
n4
+
jsrcp
;
antiperiodic_kernel
[
dst_index
]
=
src_kernel
[
src_index0
]
*
window
[
src_index0
]
-
src_kernel
[
src_index1
]
*
window
[
src_index1
]
-
src_kernel
[
src_index2
]
*
window
[
src_index2
]
+
src_kernel
[
src_index3
]
*
window
[
src_index3
];
}
}
}
public
boolean
readDCTKernels
(
EyesisCorrectionParameters
.
DCTParameters
dct_parameters
,
...
...
src/main/java/Eyesis_Correction.java
View file @
f96b0aec
...
...
@@ -2972,7 +2972,8 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
if
(!
DCT_PARAMETERS
.
showDialog
())
return
;
FactorConvKernel
factorConvKernel
=
new
FactorConvKernel
();
factorConvKernel
.
setDebugLevel
(
DEBUG_LEVEL
);
factorConvKernel
.
setTargetWindowMode
(
DCT_PARAMETERS
.
dbg_window_mode
,
DCT_PARAMETERS
.
centerWindowToTarget
);
// factorConvKernel.setTargetWindowMode(DCT_PARAMETERS.dbg_window_mode, DCT_PARAMETERS.centerWindowToTarget);
factorConvKernel
.
setTargetWindowMode
(
DCT_PARAMETERS
.
centerWindowToTarget
);
factorConvKernel
.
numIterations
=
DCT_PARAMETERS
.
LMA_steps
;
factorConvKernel
.
setAsymCompactness
(
DCT_PARAMETERS
.
compactness
,
...
...
@@ -2983,8 +2984,13 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
DCT_PARAMETERS
.
dc_weight
);
int
target_kernel_size
=
2
*
DCT_PARAMETERS
.
dct_size
-
1
;
int
target_expanded_size
=
2
*
DCT_PARAMETERS
.
dct_size
+
DCT_PARAMETERS
.
asym_size
-
2
;
// int target_expanded_size = 2*DCT_PARAMETERS.dct_size + DCT_PARAMETERS.asym_size -2;
int
target_expanded_size
=
4
*
DCT_PARAMETERS
.
dct_size
;
int
target_antiperiodic_size
=
2
*
DCT_PARAMETERS
.
dct_size
;
double
[]
target_expanded
=
null
;
double
[]
target_antiperiodic
=
null
;
if
((
EYESIS_DCT
!=
null
)
&&
EYESIS_DCT
.
kernelImageSet
()
&&
(
DCT_PARAMETERS
.
color_channel
>=
0
)){
System
.
out
.
println
(
"Using extracted target kernel"
);
double
[]
src_kernel
=
EYESIS_DCT
.
extractOneKernelFromStack
(
...
...
@@ -2992,12 +2998,33 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
DCT_PARAMETERS
.
color_channel
,
// 0..2
DCT_PARAMETERS
.
tileX
,
// horizontal number of kernel to extract
DCT_PARAMETERS
.
tileY
);
// vertical number of kernel to extract
if
((
DCT_PARAMETERS
.
decimation
==
2
)&&
(
DCT_PARAMETERS
.
decimateSigma
<
0
)){
target_expanded
=
EYESIS_DCT
.
reformatKernel2
(
src_kernel
,
// will be blured in-place
CONVOLVE_FFT_SIZE
/
2
,
// typical 64
target_expanded_size
);
// typical 15
}
else
{
target_expanded
=
EYESIS_DCT
.
reformatKernel
(
src_kernel
,
// will be blured in-place
CONVOLVE_FFT_SIZE
/
2
,
// typical 64
target_expanded_size
,
// typical 15
DCT_PARAMETERS
.
decimation
,
// typical 2
DCT_PARAMETERS
.
decimateSigma
);
}
if
(
DCT_PARAMETERS
.
normalize
)
{
double
s
=
0.0
;
for
(
int
ii
=
0
;
ii
<
target_expanded
.
length
;
ii
++){
s
+=
target_expanded
[
ii
];
}
for
(
int
ii
=
0
;
ii
<
target_expanded
.
length
;
ii
++){
target_expanded
[
ii
]/=
s
;
}
}
// target_antiperiodic
}
else
{
System
.
out
.
println
(
"Using synthesized target kernel"
);
...
...
@@ -3040,14 +3067,27 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
target_expanded
=
new
double
[
target_expanded_size
*
target_expanded_size
];
for
(
int
ii
=
0
;
ii
<
target_expanded
.
length
;
ii
++)
target_expanded
[
ii
]=
0.0
;
int
left_top_margin
=
((
DCT_PARAMETERS
.
asym_size
-
1
)/
2
);
int
left_top_margin
=
(
target_expanded_size
-
target_kernel_size
)
/
2
;
// (
(DCT_PARAMETERS.asym_size-1)/2);
for
(
int
ii
=
0
;
ii
<
target_kernel_size
;
ii
++){
for
(
int
jj
=
0
;
jj
<
target_kernel_size
;
jj
++){
target_expanded
[(
ii
+
left_top_margin
)*
target_expanded_size
+
(
jj
+
left_top_margin
)]
=
target_kernel
[
ii
*
target_kernel_size
+
jj
];
}
}
SDFA_INSTANCE
.
showArrays
(
target_kernel
,
target_kernel_size
,
target_kernel_size
,
"target_kernel"
);
SDFA_INSTANCE
.
showArrays
(
target_expanded
,
target_expanded_size
,
target_expanded_size
,
"target_expanded"
);
}
if
(
EYESIS_DCT
==
null
){
EYESIS_DCT
=
new
EyesisDCT
(
EYESIS_CORRECTIONS
,
CORRECTION_PARAMETERS
,
DCT_PARAMETERS
);
}
target_antiperiodic
=
EYESIS_DCT
.
makeAntiperiodic
(
DCT_PARAMETERS
.
dct_size
,
target_expanded
);
SDFA_INSTANCE
.
showArrays
(
target_antiperiodic
,
target_antiperiodic_size
,
target_antiperiodic_size
,
"target_antiperiodic"
);
boolean
[]
mask
=
null
;
if
(!
DCT_PARAMETERS
.
dbg_mask
.
equals
(
""
)){
...
...
@@ -3059,7 +3099,7 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
}
long
startTime
=
System
.
nanoTime
();
boolean
result
=
factorConvKernel
.
calcKernels
(
target_
expanded
,
target_
antiperiodic
,
DCT_PARAMETERS
.
asym_size
,
DCT_PARAMETERS
.
dct_size
,
DCT_PARAMETERS
.
fact_precision
,
...
...
@@ -3075,15 +3115,16 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
double
[]
sym_kernel
=
factorConvKernel
.
getSymKernel
();
double
[]
asym_kernel
=
factorConvKernel
.
getAsymKernel
();
double
[]
convolved
=
factorConvKernel
.
getConvolved
();
double
[]
target_weights
=
factorConvKernel
.
getTargetWeights
();
//
double [] target_weights = factorConvKernel.getTargetWeights();
double
[]
diff100
=
new
double
[
convolved
.
length
];
double
[]
weighted_diff100
=
new
double
[
convolved
.
length
];
//
double [] weighted_diff100 = new double [convolved.length];
for
(
int
ii
=
0
;
ii
<
convolved
.
length
;
ii
++)
{
diff100
[
ii
]=
100.0
*(
target_
expanded
[
ii
]-
convolved
[
ii
]);
weighted_diff100
[
ii
]
=
diff100
[
ii
]*
target_weights
[
ii
];
diff100
[
ii
]=
100.0
*(
target_
antiperiodic
[
ii
]-
convolved
[
ii
]);
//
weighted_diff100[ii] = diff100[ii]* target_weights[ii];
}
double
[][]
compare_kernels
=
{
target_expanded
,
convolved
,
weighted_diff100
,
target_weights
,
diff100
};
// double [][] compare_kernels = {target_expanded, convolved, weighted_diff100,target_weights, diff100};
double
[][]
compare_kernels
=
{
target_antiperiodic
,
convolved
,
diff100
};
if
(
DEBUG_LEVEL
>
0
)
{
System
.
out
.
println
(
"DCT_PARAMETERS.dct_size="
+
DCT_PARAMETERS
.
dct_size
+
" DCT_PARAMETERS.asym_size="
+
DCT_PARAMETERS
.
asym_size
);
System
.
out
.
println
(
"sym_kernel.length="
+
sym_kernel
.
length
);
...
...
@@ -3092,7 +3133,9 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
}
SDFA_INSTANCE
.
showArrays
(
sym_kernel
,
DCT_PARAMETERS
.
dct_size
,
DCT_PARAMETERS
.
dct_size
,
"sym_kernel"
);
SDFA_INSTANCE
.
showArrays
(
asym_kernel
,
DCT_PARAMETERS
.
asym_size
,
DCT_PARAMETERS
.
asym_size
,
"asym_kernel"
);
SDFA_INSTANCE
.
showArrays
(
compare_kernels
,
target_expanded_size
,
target_expanded_size
,
true
,
"compare_kernels"
);
// SDFA_INSTANCE.showArrays(compare_kernels, target_expanded_size, target_expanded_size, true, "compare_kernels");
SDFA_INSTANCE
.
showArrays
(
compare_kernels
,
target_antiperiodic_size
,
target_antiperiodic_size
,
true
,
"compare_kernels"
);
//
// SDFA_INSTANCE.showArrays(convolved, target_kernel_size, target_kernel_size, "convolved");
return
;
}
else
if
(
label
.
equals
(
"Min Kernel Factorization"
)){
...
...
@@ -3100,7 +3143,8 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
if
(!
DCT_PARAMETERS
.
showDialog
())
return
;
FactorConvKernel
factorConvKernel
=
new
FactorConvKernel
();
factorConvKernel
.
setDebugLevel
(
DEBUG_LEVEL
);
factorConvKernel
.
setTargetWindowMode
(
DCT_PARAMETERS
.
dbg_window_mode
,
DCT_PARAMETERS
.
centerWindowToTarget
);
// factorConvKernel.setTargetWindowMode(DCT_PARAMETERS.dbg_window_mode, DCT_PARAMETERS.centerWindowToTarget);
factorConvKernel
.
setTargetWindowMode
(
DCT_PARAMETERS
.
centerWindowToTarget
);
factorConvKernel
.
numIterations
=
DCT_PARAMETERS
.
LMA_steps
;
factorConvKernel
.
setAsymCompactness
(
DCT_PARAMETERS
.
compactness
,
...
...
@@ -3110,8 +3154,13 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
factorConvKernel
.
setDCWeight
(
DCT_PARAMETERS
.
dc_weight
);
int
target_kernel_size
=
2
*
DCT_PARAMETERS
.
dct_size
-
1
;
int
target_expanded_size
=
2
*
DCT_PARAMETERS
.
dct_size
+
DCT_PARAMETERS
.
asym_size
-
2
;
// int target_expanded_size = 2*DCT_PARAMETERS.dct_size + DCT_PARAMETERS.asym_size -2;
int
target_expanded_size
=
4
*
DCT_PARAMETERS
.
dct_size
;
int
target_antiperiodic_size
=
2
*
DCT_PARAMETERS
.
dct_size
;
double
[]
target_expanded
=
null
;
double
[]
target_antiperiodic
=
null
;
if
((
EYESIS_DCT
!=
null
)
&&
EYESIS_DCT
.
kernelImageSet
()
&&
(
DCT_PARAMETERS
.
color_channel
>=
0
)){
System
.
out
.
println
(
"Using extracted target kernel"
);
double
[]
src_kernel
=
EYESIS_DCT
.
extractOneKernelFromStack
(
...
...
@@ -3174,9 +3223,18 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
}
}
}
if
(
EYESIS_DCT
==
null
){
EYESIS_DCT
=
new
EyesisDCT
(
EYESIS_CORRECTIONS
,
CORRECTION_PARAMETERS
,
DCT_PARAMETERS
);
}
target_antiperiodic
=
EYESIS_DCT
.
makeAntiperiodic
(
DCT_PARAMETERS
.
dct_size
,
target_expanded
);
long
startTime
=
System
.
nanoTime
();
int
numPixels
=
factorConvKernel
.
calcKernels
(
target_
expanded
,
target_
antiperiodic
,
DCT_PARAMETERS
.
asym_size
,
DCT_PARAMETERS
.
dct_size
,
DCT_PARAMETERS
.
fact_precision
,
...
...
@@ -3197,19 +3255,19 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
double
[]
sym_kernel
=
factorConvKernel
.
getSymKernel
();
double
[]
asym_kernel
=
factorConvKernel
.
getAsymKernel
();
double
[]
convolved
=
factorConvKernel
.
getConvolved
();
double
[]
target_weights
=
factorConvKernel
.
getTargetWeights
();
//
double [] target_weights = factorConvKernel.getTargetWeights();
double
[]
diff100
=
new
double
[
convolved
.
length
];
double
[]
weighted_diff100
=
new
double
[
convolved
.
length
];
double
s0
=
0
,
s1
=
0
,
s2
=
0.0
,
s3
=
0.0
;
//
double [] weighted_diff100 = new double [convolved.length];
double
/* s0=0,s1 = 0,*/
s2
=
0.0
,
s3
=
0.0
;
for
(
int
ii
=
0
;
ii
<
convolved
.
length
;
ii
++)
{
diff100
[
ii
]=
100.0
*(
target_
expanded
[
ii
]-
convolved
[
ii
]);
weighted_diff100
[
ii
]
=
diff100
[
ii
]*
target_weights
[
ii
];
s0
+=
target_weights
[
ii
];
s1
+=
target_expanded
[
ii
]*
target_weights
[
ii
];
s2
+=
convolved
[
ii
]
*
target_weights
[
ii
]
;
s3
+=
target_
expanded
[
ii
];
diff100
[
ii
]=
100.0
*(
target_
antiperiodic
[
ii
]-
convolved
[
ii
]);
//
weighted_diff100[ii] = diff100[ii]* target_weights[ii];
//
s0+=target_weights[ii];
//
s1+=target_expanded[ii]*target_weights[ii];
s2
+=
convolved
[
ii
];
s3
+=
target_
antiperiodic
[
ii
];
if
(
DEBUG_LEVEL
>
3
)
{
System
.
out
.
println
(
ii
+
": t="
+
target_
expanded
[
ii
]+
" c="
+
convolved
[
ii
]+
" s0="
+
s0
+
" s1="
+
s1
+
" s2="
+
s2
);
System
.
out
.
println
(
ii
+
": t="
+
target_
antiperiodic
[
ii
]+
" c="
+
convolved
[
ii
]+
" s2="
+
s2
+
" s3="
+
s3
);
}
}
double
sum_asym
=
0.0
,
sum_sym
=
0.0
;
...
...
@@ -3225,14 +3283,15 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
}
}
double
[][]
compare_kernels
=
{
target_
expanded
,
convolved
,
weighted_diff100
,
target_weights
,
diff100
};
// double [][] compare_kernels = {target_expanded, convolved, weighted_diff100,target_weights, diff100};
double
[][]
compare_kernels
=
{
target_
antiperiodic
,
convolved
,
diff100
};
if
(
DEBUG_LEVEL
>
0
)
{
System
.
out
.
println
(
"DCT_PARAMETERS.dct_size="
+
DCT_PARAMETERS
.
dct_size
+
" DCT_PARAMETERS.asym_size="
+
DCT_PARAMETERS
.
asym_size
);
System
.
out
.
println
(
"sym_kernel.length="
+
sym_kernel
.
length
);
System
.
out
.
println
(
"asym_kernel.length="
+
asym_kernel
.
length
);
System
.
out
.
println
(
"convolved.length="
+
convolved
.
length
);
System
.
out
.
println
(
"weights s0="
+
s0
+
" target s1/s0="
+(
s1
/
s0
)+
"convolved s2/s0="
+(
s2
/
s0
)+
" s2/s1="
+(
s2
/
s1
));
// System.out.println("weights s0="+s0+" target s1/s0="+(s1/s0)+ "convolved s2/s0="+(s2/s0)+ " s2/s1="+(s2/s1));
System
.
out
.
println
(
"convolved s2="
+
s2
+
"target s3="
+
s3
);
System
.
out
.
println
(
"sum_asym = "
+
sum_asym
+
" sum_sym="
+
sum_sym
+
" sum_asym*sum_sym="
+
(
sum_asym
*
sum_sym
)+
" sum(target) = "
+
s3
);
}
...
...
@@ -3245,7 +3304,7 @@ private Panel panel1,panel2,panel3,panel4,panel5,panel5a, panel6,panel7,panelPos
//// SDFA_INSTANCE.showArrays(sym_kernel, DCT_PARAMETERS.dct_size, DCT_PARAMETERS.dct_size, "sym_kernel");
SDFA_INSTANCE
.
showArrays
(
asym_kernel
,
DCT_PARAMETERS
.
asym_size
,
DCT_PARAMETERS
.
asym_size
,
"asym_kernel"
);
SDFA_INSTANCE
.
showArrays
(
compare_kernels
,
target_
expanded_size
,
target_expanded
_size
,
true
,
"compare_kernels"
);
SDFA_INSTANCE
.
showArrays
(
compare_kernels
,
target_
antiperiodic_size
,
target_antiperiodic
_size
,
true
,
"compare_kernels"
);
// SDFA_INSTANCE.showArrays(convolved, target_kernel_size, target_kernel_size, "convolved");
return
;
...
...
src/main/java/FactorConvKernel.java
View file @
f96b0aec
...
...
@@ -80,7 +80,7 @@ public class FactorConvKernel {
public
double
goal_rms_pure
;
public
LMAData
lMAData
=
null
;
public
int
numLMARuns
=
0
;
public
int
target_window_mode
=
2
;
// 0 - none, 1 - square, 2 - sin, 3 - sin^2
//
public int target_window_mode = 2; // 0 - none, 1 - square, 2 - sin, 3 - sin^2
public
boolean
centerWindowToTarget
=
true
;
// center convolution weights around target kernel center
public
class
LMAArrays
{
...
...
@@ -133,7 +133,7 @@ public class FactorConvKernel {
public
double
[]
rmses
=
{-
1.0
,
-
1.0
,
-
1.0
};
// [0] - composite, [1] - "pure" (without extra "punishment"), [2] - DC error
public
double
[]
saved_rmses
=
null
;
public
double
[]
first_rmses
=
null
;
public
int
target_window_mode
=
2
;
// 0 - none, 1 - square, 2 - sin
//
public int target_window_mode = 2; // 0 - none, 1 - square, 2 - sin
public
boolean
centerWindowToTarget
=
true
;
// center asym kernel weights around target kernel center
...
...
@@ -142,8 +142,9 @@ public class FactorConvKernel {
public
LMAData
(
int
debugLevel
){
this
.
debugLevel
=
debugLevel
;
}
public
void
setTargetWindowMode
(
int
mode
,
boolean
centerWindowToTarget
){
this
.
target_window_mode
=
mode
;
// public void setTargetWindowMode(int mode, boolean centerWindowToTarget){
public
void
setTargetWindowMode
(
boolean
centerWindowToTarget
){
// this.target_window_mode = mode;
this
.
centerWindowToTarget
=
centerWindowToTarget
;
}
...
...
@@ -401,9 +402,12 @@ public class FactorConvKernel {
System
.
out
.
println
(
"getWeight(), delete jacobian"
);
}
if
((
weights
[
0
]
==
null
)
||
(
weights
[
0
].
length
!=
target_kernel
.
length
)){
/*
int xc = 0;
int yc = 0;
int
conv_size
=
asym_size
+
2
*
sym_radius
-
2
;
// int conv_size = asym_size + 2*sym_radius-2;
// int cc = conv_size/2;
int conv_size = 2*sym_radius;
int cc = conv_size/2;
if (this.centerWindowToTarget) {
double s0=0.0,sx=0.0,sy=0.0;
...
...
@@ -419,9 +423,14 @@ public class FactorConvKernel {
xc = (int) Math.round(sx/s0);
yc = (int) Math.round(sy/s0);
}
double
[]
sins
=
new
double
[
2
*
sym_radius
-
1
];
for
(
int
i
=
1
;
i
<
2
*
sym_radius
;
i
++)
sins
[
i
-
1
]
=
Math
.
sin
(
Math
.
PI
*
i
/(
2.0
*
sym_radius
));
// double [] sins = new double [2*sym_radius-1];
// for (int i = 1; i< 2*sym_radius; i++) sins[i-1] = Math.sin(Math.PI*i/(2.0 * sym_radius));
*/
weights
[
0
]
=
new
double
[
target_kernel
.
length
];
for
(
int
i
=
0
;
i
<
weights
[
0
].
length
;
i
++)
{
weights
[
0
][
i
]
=
1.0
;
}
/*
int left_margin = ((asym_size-1)/2) +xc; // inclusive
int top_margin = ((asym_size-1)/2) + yc; // inclusive
int right_margin = left_margin + (2 * sym_radius - 1); // exclusive
...
...
@@ -439,6 +448,7 @@ public class FactorConvKernel {
}
}
}
*/
}
// public boolean centerWindowToTarget = true; // center convolution weights around target kernel center
...
...
@@ -578,9 +588,12 @@ public class FactorConvKernel {
{
getWeight
(
false
);
// will invalidate (make null) fX if data is not current, otherwise just return last calculated value.
if
((
fX
==
null
)
||
justConvolved
)
{
int
conv_size
=
asym_size
+
2
*
sym_radius
-
2
;
// int conv_size = asym_size + 2*sym_radius-2;
int
conv_size
=
2
*
sym_radius
;
int
conv_len
=
conv_size
*
conv_size
;
int
sym_rad_m1
=
sym_radius
-
1
;
// 7
// int sym_rad_m1 = sym_radius - 1; // 7
int
shft
=
sym_radius
-
(
asym_size
/
2
);
int
sym_rad2
=
2
*
sym_radius
;
// 16
int
sym_rad4
=
4
*
sym_radius
;
// 32
double
[]
fX
=
new
double
[
justConvolved
?
conv_len:
this
.
weight
.
length
];
...
...
@@ -594,7 +607,8 @@ public class FactorConvKernel {
}
else
{
// calculate convolution for ci, cj
fX
[
cindx
]
=
0
;
for
(
int
ai
=
0
;
ai
<
asym_size
;
ai
++){
int
si
=
(
ci
-
ai
)
-
sym_rad_m1
;
// int si = (ci - ai) - sym_rad_m1;
int
si
=
(
ci
-
ai
)
-
shft
;
if
(
si
<
0
)
si
=
-
si
;
int
sgni
=
1
;
if
(
si
>
sym_rad2
)
si
=
sym_rad4
-
si
;
...
...
@@ -606,7 +620,8 @@ public class FactorConvKernel {
for
(
int
aj
=
0
;
aj
<
asym_size
;
aj
++){
int
aindx
=
ai
*
asym_size
+
aj
;
if
(!
skip_disabled_asym
||
kernel_masks
[
1
][
aindx
]){
int
sj
=
(
cj
-
aj
)
-
sym_rad_m1
;
// int sj = (cj - aj) - sym_rad_m1;
int
sj
=
(
cj
-
aj
)
-
shft
;
if
(
sj
<
0
)
sj
=
-
sj
;
int
sgn
=
sgni
;
if
(
sj
>
sym_rad2
)
sj
=
sym_rad4
-
sj
;
...
...
@@ -673,9 +688,12 @@ public class FactorConvKernel {
jacobian
=
new
double
[
map_from_pars
.
length
][
this
.
weight
.
length
];
// zero elements?
// calculate convolution parts, for kernels - regardless of kernels enabled/disabled
int
conv_size
=
asym_size
+
2
*
sym_radius
-
2
;
// int conv_size = asym_size + 2*sym_radius-2;
int
conv_size
=
2
*
sym_radius
;
int
conv_len
=
conv_size
*
conv_size
;
int
sym_rad_m1
=
sym_radius
-
1
;
// 7
// int sym_rad_m1 = sym_radius - 1; // 7
int
shft
=
sym_radius
-
(
asym_size
/
2
);
int
sym_rad2
=
2
*
sym_radius
;
// 16
int
sym_rad4
=
4
*
sym_radius
;
// 32
// calculate convolution part
...
...
@@ -684,7 +702,8 @@ public class FactorConvKernel {
if
(
this
.
weight
[
cindx
]
!=
0.0
){
// calculate convolution for ci, cj (skip masked out for speed)
for
(
int
ai
=
0
;
ai
<
asym_size
;
ai
++){
int
si
=
(
ci
-
ai
)
-
sym_rad_m1
;
// int si = (ci - ai) - sym_rad_m1;
int
si
=
(
ci
-
ai
)
-
shft
;
if
(
si
<
0
)
si
=
-
si
;
int
sgni
=
1
;
if
(
si
>
sym_rad2
)
si
=
sym_rad4
-
si
;
...
...
@@ -696,7 +715,8 @@ public class FactorConvKernel {
for
(
int
aj
=
0
;
aj
<
asym_size
;
aj
++){
int
aindx
=
ai
*
asym_size
+
aj
;
int
apar_indx
=
map_to_pars
[
1
][
aindx
];
int
sj
=
(
cj
-
aj
)
-
sym_rad_m1
;
// int sj = (cj - aj) - sym_rad_m1;
int
sj
=
(
cj
-
aj
)
-
shft
;
if
(
sj
<
0
)
sj
=
-
sj
;
int
sgn
=
sgni
;
if
(
sj
>
sym_rad2
)
sj
=
sym_rad4
-
sj
;
...
...
@@ -850,7 +870,8 @@ public class FactorConvKernel {
public
double
[]
getJTByDiffW
(
boolean
recalculate
)
// current convolution result of async_kernel (*) sync_kernel, extended by asym_kernel components
{
if
(
recalculate
)
{
int
conv_size
=
asym_size
+
2
*
sym_radius
-
2
;
// int conv_size = asym_size + 2*sym_radius-2;
int
conv_size
=
2
*
sym_radius
;
int
conv_len
=
conv_size
*
conv_size
;
int
len2
=
conv_len
+
this
.
weights
[
1
].
length
;
int
len3
=
len2
+
this
.
weights
[
2
].
length
;
...
...
@@ -975,8 +996,9 @@ public class FactorConvKernel {
this
.
sym_radius
=
sym_radius
;
}
public
void
setTargetWindowMode
(
int
mode
,
boolean
centerWindowToTarget
){
target_window_mode
=
mode
;
// public void setTargetWindowMode(int mode, boolean centerWindowToTarget){
public
void
setTargetWindowMode
(
boolean
centerWindowToTarget
){
// target_window_mode = mode;
this
.
centerWindowToTarget
=
centerWindowToTarget
;
}
...
...
@@ -1597,11 +1619,14 @@ public class FactorConvKernel {
int
seed_size
,
// 4*n +1 - center and 45-degree, 4*n - 4 center and 45-degree cross
double
asym_random
)
{
int
conv_size
=
asym_size
+
2
*
sym_radius
-
2
;
// int conv_size = asym_size + 2*sym_radius-2;
int
conv_size
=
2
*
sym_radius
;
int
sym_rad_m1
=
sym_radius
-
1
;
// 7
int
shft
=
sym_radius
-
(
asym_size
/
2
);
// find center of the target kernel squared value
double
s0
=
0.0
,
sx
=
0.0
,
sy
=
0.0
;
double
scx
=
0.0
,
scy
=
0.0
;
//
double scx=0.0,scy=0.0;
boolean
[]
asym_mask
=
null
;
if
(
asym_mask
==
null
)
{
asym_mask
=
new
boolean
[
asym_size
*
asym_size
];
...
...
@@ -1614,21 +1639,23 @@ public class FactorConvKernel {
s0
+=
d
;
sx
+=
d
*
j
;
sy
+=
d
*
i
;
scx
+=
d
*(
j
-
sym_rad_m1
-
asym_size
/
2
);
scy
+=
d
*(
i
-
sym_rad_m1
-
asym_size
/
2
);
//
scx += d*(j-sym_rad_m1-asym_size/2);
//
scy += d*(i-sym_rad_m1-asym_size/2);
}
}
double
xc
=
sx
/
s0
-
s
ym_rad_m1
;
double
yc
=
sy
/
s0
-
sym_rad_m1
;
double
xc
=
sx
/
s0
-
s
hft
;
// sym_rad_m1; center in asym_kernel
double
yc
=
sy
/
s0
-
s
hft
;
// s
ym_rad_m1;
int
j0
=
(
int
)
Math
.
round
(
xc
);
// should be ~ async_center
int
i0
=
(
int
)
Math
.
round
(
yc
);
// should be ~ async_center
if
(
debugLevel
>
0
){
System
.
out
.
println
(
"setInitialVector(): scx="
+(
scx
/
s0
)
+
" scy="
+(
scy
/
s0
));
//
System.out.println("setInitialVector(): scx="+(scx/s0) + " scy="+(scy/s0));
System
.
out
.
println
(
"setInitialVector(): x="
+(
sx
/
s0
)
+
" y="
+(
sy
/
s0
));
System
.
out
.
println
(
"setInitialVector(): conv_size = "
+
conv_size
+
" asym_size="
+
asym_size
);
System
.
out
.
println
(
"setInitialVector(): fj0 = "
+(
sx
/
s0
-
sym_rad_m1
)+
" j0 = "
+
j0
);
System
.
out
.
println
(
"setInitialVector(): fi0 = "
+(
sy
/
s0
-
sym_rad_m1
)+
" i0 = "
+
i0
);
// System.out.println("setInitialVector(): fj0 = "+(sx/s0 - sym_rad_m1)+" j0 = "+j0 );
// System.out.println("setInitialVector(): fi0 = "+(sy/s0 - sym_rad_m1)+" i0 = "+i0 );
System
.
out
.
println
(
"setInitialVector(): fj0 = "
+(
sx
/
s0
-
sym_radius
)+
" j0 = "
+
j0
);
System
.
out
.
println
(
"setInitialVector(): fi0 = "
+(
sy
/
s0
-
sym_radius
)+
" i0 = "
+
i0
);
}
// fit i0,j0 to asym_kernel (it should be larger)
if
((
i0
<
0
)
||
(
i0
>=
asym_size
)
||
(
i0
<
0
)
||
(
i0
>=
asym_size
)){
...
...
@@ -1688,10 +1715,8 @@ public class FactorConvKernel {
center_x
=
xc
;
// not limited by asym_size
center_y
=
yc
;
// not limited by asym_size
center_j0
=
j0
;
// center to calcualte compatess odf asymmetrical kernel
center_i0
=
i0
;
// center to calcualte compatess odf asymmetrical kernel
center_j0
=
j0
;
// center to calcualte compatess odf asymmetrical kernel
center_j0
=
j0
;
// center to calculate compactness of the asymmetrical kernel (relative to asym_kernel top left)
center_i0
=
i0
;
// center to calculate compactness of the asymmetrical kernel
if
(
debugLevel
>
2
){
for
(
int
i
=
0
;
i
<
asym_kernel
.
length
;
i
++)
{
System
.
out
.
println
(
"asym_kernel["
+
i
+
"] = "
+
asym_kernel
[
i
]);
...
...
@@ -1700,6 +1725,11 @@ public class FactorConvKernel {
for
(
int
i
=
0
;
i
<
asym_kernel
.
length
;
i
++)
{
if
(!
asym_mask
[
i
])
asym_kernel
[
i
]
=
Double
.
NaN
;
}
if
(
debugLevel
>
2
){
for
(
int
i
=
0
;
i
<
asym_kernel
.
length
;
i
++)
{
System
.
out
.
println
(
"- asym_kernel["
+
i
+
"] = "
+
asym_kernel
[
i
]);
}
}
double
[]
sym_kernel
=
new
double
[
sym_radius
*
sym_radius
];
...
...
@@ -1708,19 +1738,53 @@ public class FactorConvKernel {
sym_kernel
[
i
]
=
0.0
;
sym_kernel_count
[
i
]
=
0
;
}
// int target_x_shft = j0+(conv_size-asym_size)/2 - sym_rad_m1;
// int target_y_shft = i0+(conv_size-asym_size)/2 - sym_rad_m1;
// int target_x_shft = j0+ sym_radius -asym_size/2 - sym_rad_m1;
int
target_x_shft
=
j0
-
asym_size
/
2
+
1
;
int
target_y_shft
=
i0
-
asym_size
/
2
+
1
;
// int sym_rad_m1 = sym_radius - 1; // 7
// int shft = sym_radius - (asym_size/2);
if
(
debugLevel
>
2
)
{
System
.
out
.
println
(
" target_x_shft = "
+
target_x_shft
+
" target_y_shft = "
+
target_y_shft
+
" i0="
+
i0
+
" j0="
+
j0
+
" asym_size="
+
asym_size
);
}
for
(
int
i
=
0
;
i
<
(
2
*
sym_radius
-
1
);
i
++
){
for
(
int
j
=
0
;
j
<
(
2
*
sym_radius
-
1
);
j
++
){
int
indx
=((
i
>=
sym_rad_m1
)?
(
i
-
sym_rad_m1
):
(
sym_rad_m1
-
i
))
*
sym_radius
+
((
j
>=
sym_rad_m1
)?
(
j
-
sym_rad_m1
):
(
sym_rad_m1
-
j
));
sym_kernel
[
indx
]
+=
target_kernel
[
conv_size
*(
i
+
i0
)
+
(
j
+
j0
)];
// int si = (i >= sym_rad_m1)? (i - sym_rad_m1): (sym_rad_m1 - i);
// int sj = (j >= sym_rad_m1)? (j - sym_rad_m1): (sym_rad_m1 - j);
int
si
=
(
i
>=
sym_rad_m1
)?
(
i
-
sym_rad_m1
):
(
sym_rad_m1
-
i
);
int
sj
=
(
j
>=
sym_rad_m1
)?
(
j
-
sym_rad_m1
):
(
sym_rad_m1
-
j
);
int
indx
=
si
*
sym_radius
+
sj
;
int
target_i
=
i
+
target_y_shft
;
int
target_j
=
j
+
target_x_shft
;
int
sgn
=
1
;
if
((
target_i
<
0
)
||
(
target_i
>=
conv_size
))
{
target_i
=
(
target_i
+
conv_size
)%
conv_size
;
sgn
=-
sgn
;
}
if
((
target_j
<
0
)
||
(
target_j
>=
conv_size
))
{
target_j
=
(
target_j
+
conv_size
)%
conv_size
;
sgn
=-
sgn
;
}
sym_kernel
[
indx
]
+=
sgn
*
target_kernel
[
conv_size
*
target_i
+
target_j
];
sym_kernel_count
[
indx
]++;
if
((
debugLevel
>
2
)
&&
(
indx
==
0
))
{
if
(
debugLevel
>
0
)
System
.
out
.
println
(
"1.sym_kernel[0] = "
+
sym_kernel
[
0
]+
" i="
+
i
+
" j="
+
j
+
" i0="
+
i0
+
" j0="
+
j0
+
" conv_size="
+
conv_size
+
" target_kernel["
+(
conv_size
*(
i
+
i0
)
+
(
j
+
j0
))+
"] = "
+
target_kernel
[(
conv_size
*(
i
+
i0
)
+
(
j
+
j0
))]);
}
sym_kernel_count
[
indx
]++;
" target_kernel["
+(
conv_size
*
target_i
+
target_j
)+
"] = "
+
target_kernel
[
conv_size
*
target_i
+
target_j
]);
}
if
(
debugLevel
>
2
)
{
System
.
out
.
println
(
"2.sgn="
+
sgn
+
" sym_kernel[0] = "
+
sym_kernel
[
0
]+
" i="
+
i
+
" j="
+
j
+
" si="
+
si
+
" sj="
+
sj
+
" indx="
+
indx
+
" target_i="
+
target_i
+
" target_j="
+
target_j
+
" target_kernel["
+(
conv_size
*
target_i
+
target_j
)+
"] = "
+
target_kernel
[
conv_size
*
target_i
+
target_j
]+
" sym_kernel_count["
+
indx
+
"]="
+
sym_kernel_count
[
indx
]+
" sym_kernel["
+
indx
+
"]="
+
sym_kernel
[
indx
]);
}
}
}
if
(
debugLevel
>
2
)
System
.
out
.
println
(
"sym_kernel[0] = "
+
sym_kernel
[
0
]+
" sym_kernel_count[0] = "
+
sym_kernel_count
[
0
]);
for
(
int
i
=
0
;
i
<
sym_kernel
.
length
;
i
++){
if
(
sym_kernel_count
[
i
]
>
0
)
sym_kernel
[
i
]
/=
sym_kernel_count
[
i
];
...
...
@@ -1728,6 +1792,21 @@ public class FactorConvKernel {
}
if
(
debugLevel
>
2
)
System
.
out
.
println
(
"sym_kernel[0] = "
+
sym_kernel
[
0
]);
double
[][]
kernels
=
{
sym_kernel
,
asym_kernel
};
if
(
debugLevel
>
2
){
for
(
int
i
=
0
;
i
<
sym_kernel
.
length
;
i
++)
{
System
.
out
.
println
(
"sym_kernel["
+
i
+
"] = "
+
sym_kernel
[
i
]);
}
System
.
out
.
println
(
"sym_kernel.length="
+
sym_kernel
.
length
);
System
.
out
.
println
(
"asym_kernel.length="
+
asym_kernel
.
length
);
System
.
out
.
println
(
"target_kernel.length="
+
target_kernel
.
length
);
showDoubleFloatArrays
sdfa_instance
=
new
showDoubleFloatArrays
();
// just for debugging?
sdfa_instance
.
showArrays
(
sym_kernel
,
sym_radius
,
sym_radius
,
"init-sym_kernel"
);
sdfa_instance
.
showArrays
(
asym_kernel
,
asym_size
,
asym_size
,
"init-asym_kernel"
);
sdfa_instance
.
showArrays
(
target_kernel
,
conv_size
,
conv_size
,
"target_kernel"
);
}
return
kernels
;
}
...
...
@@ -1751,8 +1830,9 @@ public class FactorConvKernel {
private
void
initLevenbergMarquardt
(
double
fact_precision
,
int
seed_size
,
double
asym_random
){
lMAData
=
new
LMAData
(
debugLevel
);
lMAData
.
setTarget
(
target_kernel
);
lMAData
.
setTargetWindowMode
(
target_window_mode
,
centerWindowToTarget
);
double
[][]
kernels
=
setInitialVector
(
target_kernel
,
seed_size
,
asym_random
);
// should be (asym_size + 2*sym_radius-1)**2
// lMAData.setTargetWindowMode(target_window_mode, centerWindowToTarget);
lMAData
.
setTargetWindowMode
(
centerWindowToTarget
);
double
[][]
kernels
=
setInitialVector
(
target_kernel
,
seed_size
,
asym_random
);
// should be (asym_size + 2*sym_radius-1)**2 - not anymore
sym_kernel_scale
=
kernels
[
0
][
0
];
for
(
int
i
=
0
;
i
<
kernels
[
0
].
length
;
i
++)
if
(!
Double
.
isNaN
(
kernels
[
0
][
i
]))
kernels
[
0
][
i
]
/=
sym_kernel_scale
;
for
(
int
i
=
0
;
i
<
kernels
[
1
].
length
;
i
++)
if
(!
Double
.
isNaN
(
kernels
[
1
][
i
]))
kernels
[
1
][
i
]
*=
sym_kernel_scale
;
...
...
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