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
8ad607c6
Commit
8ad607c6
authored
Jul 29, 2022
by
Andrey Filippov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
working on motion vectors strength equalization
parent
ad8191c2
Changes
5
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
606 additions
and
19 deletions
+606
-19
ImageDtt.java
src/main/java/com/elphel/imagej/tileprocessor/ImageDtt.java
+13
-0
IntersceneMatchParameters.java
...lphel/imagej/tileprocessor/IntersceneMatchParameters.java
+3
-3
OpticalFlow.java
...ain/java/com/elphel/imagej/tileprocessor/OpticalFlow.java
+337
-7
QuadCLT.java
src/main/java/com/elphel/imagej/tileprocessor/QuadCLT.java
+219
-4
QuadCLTCPU.java
...main/java/com/elphel/imagej/tileprocessor/QuadCLTCPU.java
+34
-5
No files found.
src/main/java/com/elphel/imagej/tileprocessor/ImageDtt.java
View file @
8ad607c6
...
...
@@ -2802,6 +2802,19 @@ public class ImageDtt extends ImageDttCPU {
}
if
(
half_disparity
>
0.0
)
{
// scale by disparity
mv
[
nTile
][
2
]
*=
half_disparity
/(
half_disparity
+
pxd
[
nTile
][
2
]);
// } else { // temporary, testing, make equalization?
// double disp0 = 5.0;
// if (pxd[nTile][2] > disp0) {
// mv[nTile][2] *= pxd[nTile][2]/disp0;
// }
/*
} else {
double disp0 = 5.0;
if (pxd[nTile][2] > disp0) {
mv[nTile][2] *= 10; // pxd[nTile][2]/disp0;
}
*/
}
if
((
half_avg_diff
>
0.0
)
&&(
num_neibs
>
0
))
{
double
dx
=
mv
[
nTile
][
0
]
-
sx
/
num_neibs
;
...
...
src/main/java/com/elphel/imagej/tileprocessor/IntersceneMatchParameters.java
View file @
8ad607c6
...
...
@@ -185,8 +185,8 @@ public class IntersceneMatchParameters {
public
double
min_str_fpn
=
0.2
;
// 0.25; // minimal correlation strength for all but TD-accumulated layer
public
double
min_str_sum_fpn
=
0.5
;
// 0.8; // minimal correlation strength for TD-accumulated layer
public
double
min_str
=
0.18
;
// tiles w/o FPN: minimal correlation strength for all but TD-accumulated layer
public
double
min_str_sum
=
0.33
;
// tiles w/o FPN: minimal correlation strength for TD-accumulated layer
public
double
min_str
=
0.1
2
;
//1
8; // tiles w/o FPN: minimal correlation strength for all but TD-accumulated layer
public
double
min_str_sum
=
0.
2
;
// 0.
33; // tiles w/o FPN: minimal correlation strength for TD-accumulated layer
public
int
min_neibs
=
2
;
// minimal number of strong neighbors (> min_str)
public
double
weight_zero_neibs
=
0.2
;
// Reduce weight for no-neib (1.0 for all 8)
...
...
@@ -194,7 +194,7 @@ public class IntersceneMatchParameters {
public
double
half_avg_diff
=
0.2
;
// when L2 of x,y difference from average of neibs - reduce twice
// Detect initial match
public
double
min_ref_str
=
0.22
;
// For orientations: use only tiles of the reference scene DSI_MAIN is stronger
public
double
min_ref_str
=
0.
15
;
// 0.
22; // For orientations: use only tiles of the reference scene DSI_MAIN is stronger
public
int
pix_step
=
4
;
// Azimuth/tilt search step in pixels
public
int
search_rad
=
10
;
// Search radius in steps
public
double
maybe_sum
=
1.0
;
// minimal sum of strengths (will search for the best)
...
...
src/main/java/com/elphel/imagej/tileprocessor/OpticalFlow.java
View file @
8ad607c6
This diff is collapsed.
Click to expand it.
src/main/java/com/elphel/imagej/tileprocessor/QuadCLT.java
View file @
8ad607c6
...
...
@@ -45,6 +45,7 @@ import com.elphel.imagej.cameras.CLTParameters;
import
com.elphel.imagej.cameras.ColorProcParameters
;
import
com.elphel.imagej.cameras.EyesisCorrectionParameters
;
import
com.elphel.imagej.common.DoubleGaussianBlur
;
import
com.elphel.imagej.common.PolynomialApproximation
;
import
com.elphel.imagej.common.ShowDoubleFloatArrays
;
import
com.elphel.imagej.correction.CorrectionColorProc
;
import
com.elphel.imagej.correction.EyesisCorrections
;
...
...
@@ -2426,20 +2427,234 @@ public class QuadCLT extends QuadCLTCPU {
}
double
[]
offsets_old
=
ref_scene
.
getLwirOffsets
();
double
[]
scales_old
=
ref_scene
.
getLwirScales
();
double
[]
scales2_old
=
ref_scene
.
getLwirScales2
();
double
[]
offsets_new
=
new
double
[
num_sens
];
double
[]
scales_new
=
new
double
[
num_sens
];
double
[]
scales2_new
=
new
double
[
num_sens
];
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
scales_new
[
n
]
=
scales_old
[
n
]/
scales
[
n
];
// offsets_new[n] = offsets_old[n] - offsets[n] / scales_old[n];
offsets_new
[
n
]
=
offsets_old
[
n
]
+
offsets
[
n
]
/
scales_old
[
n
];
}
System
.
out
.
println
(
"calibratePhotometric() Updated calibration:"
);
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
System
.
out
.
println
(
String
.
format
(
"%2d: %8.4f %8.6f
"
,
n
,
offsets_new
[
n
],
scales
_new
[
n
]));
System
.
out
.
println
(
String
.
format
(
"%2d: %8.4f %8.6f
%8.6f"
,
n
,
offsets_new
[
n
],
scales_new
[
n
],
scales2
_new
[
n
]));
}
System
.
out
.
println
();
ref_scene
.
setLwirOffsets
(
offsets_new
);
ref_scene
.
setLwirOffsets
(
offsets_new
);
ref_scene
.
setLwirScales
(
scales_new
);
ref_scene
.
setLwirScales2
(
scales2_new
);
return
true
;
}
public
static
boolean
calibratePhotometric2
(
// quadratic
CLTParameters
clt_parameters
,
final
QuadCLT
ref_scene
,
// now - may be null - for testing if scene is rotated ref
final
double
min_strength
,
final
double
max_diff
,
// 30.0
final
int
num_refines
,
// 2
final
double
[][]
combo_dsn_final
,
// double [][] combo_dsn_final, // dls,
int
threadsMax
,
final
boolean
debug
)
{
// filter disparity by LMA only, same bg and fg
double
[]
disparity_ref
=
combo_dsn_final
[
OpticalFlow
.
COMBO_DSN_INDX_DISP
].
clone
();
for
(
int
i
=
00
;
i
<
disparity_ref
.
length
;
i
++)
{
if
(
combo_dsn_final
[
OpticalFlow
.
COMBO_DSN_INDX_STRENGTH
][
i
]
<
min_strength
)
{
disparity_ref
[
i
]
=
Double
.
NaN
;
}
else
if
(
combo_dsn_final
[
OpticalFlow
.
COMBO_DSN_INDX_DISP_BG_ALL
][
i
]
!=
combo_dsn_final
[
OpticalFlow
.
COMBO_DSN_INDX_DISP
][
i
])
{
disparity_ref
[
i
]
=
Double
.
NaN
;
}
}
ImagePlus
img_ref
=
renderGPUFromDSI
(
-
1
,
// final int sensor_mask,
false
,
// final boolean merge_channels,
null
,
// final Rectangle full_woi_in, // show larger than sensor WOI in tiles (or null)
clt_parameters
,
// CLTParameters clt_parameters,
disparity_ref
,
// double [] disparity_ref,
OpticalFlow
.
ZERO3
,
// final double [] scene_xyz, // camera center in world coordinates
OpticalFlow
.
ZERO3
,
// final double [] scene_atr, // camera orientation relative to world frame
ref_scene
,
// final QuadCLT scene,
ref_scene
,
// final QuadCLT ref_scene, // now - may be null - for testing if scene is rotated ref
false
,
// final boolean toRGB,
true
,
// final boolean show_nan,
"PHOTOMETRIC"
,
// String suffix,
threadsMax
,
// int threadsMax,
-
2
);
// final int debugLevel);
img_ref
.
show
();
ImageStack
imageStack
=
img_ref
.
getStack
();
int
num_sens
=
imageStack
.
getSize
();
float
[]
fpixels
;
double
[][]
dpixels
=
new
double
[
num_sens
][];
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
fpixels
=
(
float
[])
imageStack
.
getPixels
(
n
+
1
);
dpixels
[
n
]
=
new
double
[
fpixels
.
length
];
for
(
int
i
=
0
;
i
<
fpixels
.
length
;
i
++)
{
dpixels
[
n
][
i
]
=
fpixels
[
i
];
}
}
boolean
quadratic
=
true
;
// double [][] dpix_orig = new double[num_sens][];
// for (int n = 0; n < num_sens; n++) {
// dpix_orig[n] = dpixels[n].clone();
// }
int
width
=
img_ref
.
getWidth
();
int
height
=
img_ref
.
getHeight
();
int
len
=
width
*
height
;
double
[]
avg_pix
=
new
double
[
len
];
double
[]
offsets
=
new
double
[
dpixels
.
length
];
double
[]
scales
=
new
double
[
dpixels
.
length
];
double
s0
=
0.0
;
double
sx
=
0.0
;
double
sx2
=
0.0
;
double
[]
sy
=
new
double
[
num_sens
];
double
[]
sxy
=
new
double
[
num_sens
];
boolean
[]
good_pix
=
new
boolean
[
len
];
double
[][]
pa_coeff
=
new
double
[
num_sens
][];
Arrays
.
fill
(
good_pix
,
true
);
for
(
int
nref
=
0
;
nref
<
num_refines
;
nref
++)
{
Arrays
.
fill
(
avg_pix
,
0.0
);
int
num_good
=
0
;
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
avg_pix
[
i
]+=
dpixels
[
n
][
i
];
good_pix
[
i
]
&=
!
Double
.
isNaN
(
dpixels
[
n
][
i
]);
}
avg_pix
[
i
]
/=
dpixels
.
length
;
if
(
good_pix
[
i
])
{
num_good
++;
}
}
double
[][]
pa_data
=
new
double
[
num_good
][
2
];
for
(
int
nsens
=
0
;
nsens
<
num_sens
;
nsens
++)
{
int
indx
=
0
;
for
(
int
i
=
0
;
i
<
len
;
i
++)
if
(
good_pix
[
i
]){
pa_data
[
indx
][
0
]
=
dpixels
[
nsens
][
i
];
pa_data
[
indx
][
1
]
=
avg_pix
[
i
];
indx
++;
}
// quadratic
pa_coeff
[
nsens
]
=(
new
PolynomialApproximation
(
0
)).
polynomialApproximation1d
(
pa_data
,
quadratic
?
2
:
1
);
}
if
(
debug
)
{
System
.
out
.
println
(
"calibratePhotometric() nref="
+
nref
);
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
System
.
out
.
println
(
String
.
format
(
"%2d: %8.4f %8.6f %8.6f"
,
n
,
pa_coeff
[
n
][
0
],
pa_coeff
[
n
][
1
],
1
e6
*
pa_coeff
[
n
][
2
]));
}
System
.
out
.
println
();
double
[][]
diffs
=
new
double
[
num_sens
][
len
];
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
Arrays
.
fill
(
diffs
[
n
],
Double
.
NaN
);
}
for
(
int
i
=
0
;
i
<
len
;
i
++)
if
(
good_pix
[
i
])
{
// if (!Double.isNaN(avg_pix[i])){
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
// diffs[n][i] = dpixels[n][i] - (scales[n] * avg_pix[i] + offsets[n]);
diffs
[
n
][
i
]
=
-(
avg_pix
[
i
]
-
pa_coeff
[
n
][
2
]
*
dpixels
[
n
][
i
]
*
dpixels
[
n
][
i
]
-
pa_coeff
[
n
][
1
]
*
dpixels
[
n
][
i
]
-
pa_coeff
[
n
][
0
]);
}
}
(
new
ShowDoubleFloatArrays
()).
showArrays
(
// out of boundary 15
diffs
,
width
,
height
,
true
,
"photometric-quad-err"
+
nref
);
}
Arrays
.
fill
(
offsets
,
0.0
);
Arrays
.
fill
(
scales
,
0.0
);
s0
=
0.0
;
sx
=
0.0
;
sx2
=
0.0
;
Arrays
.
fill
(
sy
,
0.0
);
Arrays
.
fill
(
sxy
,
0.0
);
for
(
int
i
=
0
;
i
<
len
;
i
++)
if
(
good_pix
[
i
])
{
// !Double.isNaN(avg_pix[i])){
s0
+=
1.0
;
sx
+=
avg_pix
[
i
];
sx2
+=
avg_pix
[
i
]
*
avg_pix
[
i
];
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
sy
[
n
]
+=
dpixels
[
n
][
i
];
sxy
[
n
]
+=
dpixels
[
n
][
i
]
*
avg_pix
[
i
];
}
}
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
double
d
=
s0
*
sx2
+
sx
*
sy
[
n
];
scales
[
n
]
=
(
sxy
[
n
]
*
s0
+
sy
[
n
]
*
sy
[
n
])
/
d
;
offsets
[
n
]
=
(
sy
[
n
]
*
sx2
-
sxy
[
n
]*
sx
)
/
d
;
}
if
(
debug
)
{
System
.
out
.
println
(
"calibratePhotometric() nref="
+
nref
);
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
System
.
out
.
println
(
String
.
format
(
"%2d: %8.4f %8.6f"
,
n
,
offsets
[
n
],
scales
[
n
]));
}
System
.
out
.
println
();
double
[][]
diffs
=
new
double
[
num_sens
][
len
];
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
Arrays
.
fill
(
diffs
[
n
],
Double
.
NaN
);
}
for
(
int
i
=
0
;
i
<
len
;
i
++)
if
(
good_pix
[
i
])
{
// if (!Double.isNaN(avg_pix[i])){
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
diffs
[
n
][
i
]
=
dpixels
[
n
][
i
]
-
(
scales
[
n
]
*
avg_pix
[
i
]
+
offsets
[
n
]);
}
}
(
new
ShowDoubleFloatArrays
()).
showArrays
(
// out of boundary 15
diffs
,
width
,
height
,
true
,
"photometric-err"
+
nref
);
// dpix_orig[n] = dpixels[n].clone();
double
[][]
corrected
=
new
double
[
num_sens
][
len
];
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
Arrays
.
fill
(
corrected
[
n
],
Double
.
NaN
);
for
(
int
i
=
0
;
i
<
len
;
i
++)
if
(!
Double
.
isNaN
(
dpixels
[
n
][
i
])){
corrected
[
n
][
i
]
=
(
dpixels
[
n
][
i
]
-
offsets
[
n
])/
scales
[
n
];
}
}
(
new
ShowDoubleFloatArrays
()).
showArrays
(
// out of boundary 15
corrected
,
width
,
height
,
true
,
"photometric-corr"
+
nref
);
}
//max_diff
if
(
nref
<
(
num_refines
-
1
))
{
for
(
int
i
=
0
;
i
<
len
;
i
++)
if
(
good_pix
[
i
])
{
// !Double.isNaN(avg_pix[i])){
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
double
diff
=
Math
.
abs
(
dpixels
[
n
][
i
]
-
(
scales
[
n
]
*
avg_pix
[
i
]
+
offsets
[
n
]));
if
(
diff
>
max_diff
)
{
// dpixels[n][i] = Double.NaN;
good_pix
[
i
]
=
false
;
break
;
}
}
}
}
}
double
[]
offsets_old
=
ref_scene
.
getLwirOffsets
();
double
[]
scales_old
=
ref_scene
.
getLwirScales
();
double
[]
scales2_old
=
ref_scene
.
getLwirScales2
();
double
[]
offsets_new
=
new
double
[
num_sens
];
double
[]
scales_new
=
new
double
[
num_sens
];
double
[]
scales2_new
=
new
double
[
num_sens
];
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
scales_new
[
n
]
=
scales_old
[
n
]/
scales
[
n
];
offsets_new
[
n
]
=
offsets_old
[
n
]
+
offsets
[
n
]
/
scales_old
[
n
];
}
System
.
out
.
println
(
"calibratePhotometric() Updated calibration:"
);
for
(
int
n
=
0
;
n
<
num_sens
;
n
++)
{
System
.
out
.
println
(
String
.
format
(
"%2d: %8.4f %8.6f %8.6f"
,
n
,
offsets_new
[
n
],
scales_new
[
n
],
scales2_new
[
n
]));
}
System
.
out
.
println
();
ref_scene
.
setLwirOffsets
(
offsets_new
);
ref_scene
.
setLwirScales
(
scales_new
);
ref_scene
.
setLwirScales2
(
scales2_new
);
return
true
;
}
...
...
src/main/java/com/elphel/imagej/tileprocessor/QuadCLTCPU.java
View file @
8ad607c6
...
...
@@ -153,7 +153,7 @@ public class QuadCLTCPU {
boolean
is_aux
=
false
;
double
[]
lwir_offsets
=
null
;
// per image subtracted values
double
[]
lwir_scales
=
null
;
// per image scales
//
double [] lwir_scales2 = null; // per image quadratic scales
double
[]
lwir_scales2
=
null
;
// per image quadratic scales
@Deprecated
double
lwir_offset
=
Double
.
NaN
;
// average of lwir_offsets[]
// hot and cold are calculated during autoranging (when generating 4 images for restored (added lwir_offset)
...
...
@@ -266,6 +266,7 @@ public class QuadCLTCPU {
// this.is_mono = qParent.is_mono;
this
.
lwir_offsets
=
ErsCorrection
.
clone1d
(
qParent
.
lwir_offsets
);
this
.
lwir_scales
=
ErsCorrection
.
clone1d
(
qParent
.
lwir_scales
);
this
.
lwir_scales2
=
ErsCorrection
.
clone1d
(
qParent
.
lwir_scales2
);
this
.
lwir_offset
=
qParent
.
lwir_offset
;
this
.
lwir_cold_hot
=
ErsCorrection
.
clone1d
(
qParent
.
lwir_cold_hot
);
this
.
ds_from_main
=
ErsCorrection
.
clone2d
(
qParent
.
ds_from_main
);
...
...
@@ -1798,6 +1799,12 @@ public class QuadCLTCPU {
}
return
lwir_scales
;
}
public
double
[]
getLwirScales2
()
{
if
(
lwir_scales2
==
null
)
{
lwir_scales2
=
new
double
[
getNumSensors
()];
// all 0;
}
return
lwir_scales2
;
}
public
void
setLwirOffsets
(
double
[]
offsets
)
{
lwir_offsets
=
offsets
;
// will need to update properties!
if
(
offsets
!=
null
)
{
...
...
@@ -1812,6 +1819,9 @@ public class QuadCLTCPU {
public
void
setLwirScales
(
double
[]
scales
)
{
lwir_scales
=
scales
;
// will need to update properties!
}
public
void
setLwirScales2
(
double
[]
scales2
)
{
lwir_scales2
=
scales2
;
// will need to update properties!
}
public
double
[]
getColdHot
()
{
// USED in lwir
return
lwir_cold_hot
;
...
...
@@ -2011,6 +2021,10 @@ public class QuadCLTCPU {
properties
.
setProperty
(
prefix
+
"lwir_scales"
,
IntersceneMatchParameters
.
doublesToString
(
this
.
lwir_scales
));
}
if
(
this
.
lwir_scales2
!=
null
)
{
properties
.
setProperty
(
prefix
+
"lwir_scales2"
,
IntersceneMatchParameters
.
doublesToString
(
this
.
lwir_scales2
));
}
}
...
...
@@ -2053,6 +2067,12 @@ public class QuadCLTCPU {
properties
.
setProperty
(
this_prefix
+
"lwir_scales"
,
IntersceneMatchParameters
.
doublesToString
(
this
.
lwir_scales
));
}
if
(
other_properties
.
getProperty
(
other_prefix
+
"lwir_scales2"
)!=
null
)
{
this
.
lwir_scales2
=
IntersceneMatchParameters
.
StringToDoubles
(
other_properties
.
getProperty
(
other_prefix
+
"lwir_scales2"
),
0
);
properties
.
setProperty
(
this_prefix
+
"lwir_scales2"
,
IntersceneMatchParameters
.
doublesToString
(
this
.
lwir_scales2
));
}
/*
...
...
@@ -2173,6 +2193,10 @@ public class QuadCLTCPU {
this
.
lwir_scales
=
IntersceneMatchParameters
.
StringToDoubles
(
properties
.
getProperty
(
prefix
+
"lwir_scales"
),
0
);
}
if
(
properties
.
getProperty
(
prefix
+
"lwir_scales2"
)!=
null
)
{
this
.
lwir_scales2
=
IntersceneMatchParameters
.
StringToDoubles
(
properties
.
getProperty
(
prefix
+
"lwir_scales2"
),
0
);
}
...
...
@@ -5572,11 +5596,13 @@ public class QuadCLTCPU {
}
double
[]
offsets
=
getLwirOffsets
();
double
[]
scales
=
getLwirScales
();
double
[]
scales2
=
getLwirScales2
();
channelLwirApplyEqualize
(
// now apply (was part of channelLwirEqualize() )
channelFiles
,
// int [] channelFiles,
imp_srcs
,
// ImagePlus [] imp_srcs,
offsets
,
// double [] offsets,
scales
,
// double [] scales,
scales2
,
// double [] scales2,
threadsMax
,
debugLevel
);
}
...
...
@@ -6162,6 +6188,7 @@ public class QuadCLTCPU {
ImagePlus
[]
imp_srcs
,
double
[]
offsets
,
double
[]
scales
,
double
[]
scales2
,
final
int
threadsMax
,
int
debugLevel
){
final
Thread
[]
threads
=
ImageDtt
.
newThreadArray
(
threadsMax
);
...
...
@@ -6174,9 +6201,11 @@ public class QuadCLTCPU {
if
(
nFile
>=
0
)
{
float
fd
=
(
float
)
offsets
[
srcChannel
];
float
fscale
=
(
float
)
scales
[
srcChannel
];
float
fscale2
=
(
float
)
scales2
[
srcChannel
];
float
[]
pixels
=
(
float
[])
imp_srcs
[
srcChannel
].
getProcessor
().
getPixels
();
for
(
int
i
=
0
;
i
<
pixels
.
length
;
i
++)
{
pixels
[
i
]
=
(
pixels
[
i
]
-
fd
)
*
fscale
;
float
poffs
=
(
pixels
[
i
]
-
fd
);
pixels
[
i
]
=
poffs
*
fscale
+
poffs
*
poffs
*
fscale2
;
}
}
}
...
...
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