Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Open sidebar
GEPARD
GEPARD
Commits
40f9393a
Commit
40f9393a
authored
Dec 09, 2019
by
Josef Brandt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Global scale factor for image in segmentation
parent
e9854f67
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
96 additions
and
77 deletions
+96
-77
detectionview.py
detectionview.py
+30
-21
segmentation.py
segmentation.py
+66
-56
No files found.
detectionview.py
View file @
40f9393a
...
...
@@ -178,13 +178,6 @@ class ImageView(QtWidgets.QLabel):
self
.
drag
=
"add"
p0
=
event
.
pos
()
self
.
appendSeedPoints
(
p0
)
# print(p0)
# if self.drag =="add":
# self.seedpoints.append([p0.x(), p0.y(), self.seedradius])
# elif self.drag =="delete":
# self.seeddeletepoints.append([p0.x(), p0.y(), self.seedradius])
# elif self.drag == "remove":
# self.removeSeeds([p0.x(), p0.y()])
self
.
update
()
super
().
mousePressEvent
(
event
)
...
...
@@ -193,12 +186,6 @@ class ImageView(QtWidgets.QLabel):
if
self
.
drag
:
p0
=
event
.
pos
()
self
.
appendSeedPoints
(
p0
)
# if self.drag == "add":
# self.seedpoints.append([p0.x(),p0.y(),self.seedradius])
# elif self.drag == "delete":
# self.seeddeletepoints.append([p0.x(),p0.y(),self.seedradius])
# else:
# self.removeSeeds([p0.x(),p0.y()])
self
.
update
()
super
().
mouseMoveEvent
(
event
)
...
...
@@ -318,13 +305,31 @@ class ParticleDetectionView(QtWidgets.QWidget):
self
.
showseedpoints
.
setChecked
(
True
)
self
.
setImageCenter
()
self
.
globalScaleFactor
=
QtWidgets
.
QDoubleSpinBox
(
self
)
self
.
globalScaleLabel
=
QtWidgets
.
QLabel
(
''
)
self
.
globalScaleFactor
.
valueChanged
.
connect
(
self
.
updatePixelScaleLabel
)
self
.
globalScaleFactor
.
setMinimum
(
0.01
)
self
.
globalScaleFactor
.
setMaximum
(
1.
)
self
.
globalScaleFactor
.
setSingleStep
(
0.1
)
self
.
globalScaleFactor
.
setDecimals
(
2
)
self
.
globalScaleFactor
.
setValue
(
1.
)
self
.
globalScaleFactor
.
valueChanged
.
connect
(
self
.
autoUpdateIfDesired
)
globalScaleValueFunc
=
makeValueLambda
(
self
.
globalScaleFactor
.
value
)
self
.
detectParamsGroup
=
QtWidgets
.
QGroupBox
(
"Detection settings"
,
self
)
grid
=
QtWidgets
.
QGridLayout
()
self
.
parameters
=
[]
self
.
parameters
.
append
([
self
.
globalScaleFactor
,
'globalScaleFactor'
,
globalScaleValueFunc
,
None
])
checkBoxesToLink
=
{}
grid
.
addWidget
(
self
.
globalScaleLabel
,
0
,
0
,
QtCore
.
Qt
.
AlignLeft
)
grid
.
addWidget
(
self
.
globalScaleFactor
,
0
,
1
,
QtCore
.
Qt
.
AlignRight
)
# create editable parameters:
for
i
,
p
in
enumerate
(
self
.
seg
.
parlist
):
i
+=
1
label
,
colstretch
=
None
,
1
if
p
.
name
==
"contrastCurve"
:
paramui
=
HistWidget
(
lambda
:
self
.
seg
.
calculateHist
(
self
.
seg
.
convert2Gray
(
self
.
subimg
)),
...
...
@@ -392,8 +397,6 @@ class ParticleDetectionView(QtWidgets.QWidget):
box
.
stateChanged
.
connect
(
makeEnableLambda
(
box
,
p
[
3
]))
p
[
3
].
setEnabled
(
box
.
isChecked
())
label
=
QtWidgets
.
QLabel
(
"Seed radius"
,
self
)
grid
.
addWidget
(
label
,
i
+
1
,
0
,
QtCore
.
Qt
.
AlignLeft
)
grid
.
addWidget
(
self
.
seedradiusedit
,
i
+
1
,
1
,
QtCore
.
Qt
.
AlignLeft
)
...
...
@@ -445,6 +448,11 @@ class ParticleDetectionView(QtWidgets.QWidget):
self
.
setLayout
(
hbox
)
self
.
setWindowTitle
(
"Particle Detection"
)
def
updatePixelScaleLabel
(
self
):
newScale
=
self
.
dataset
.
getPixelScale
(
self
.
dataset
.
imagescanMode
)
/
self
.
globalScaleFactor
.
value
()
newScale
=
np
.
round
(
newScale
,
2
)
self
.
globalScaleLabel
.
setText
(
f
'Scale for image (PixelScale:
{
newScale
}
µm/px)'
)
def
saveDetectParams
(
self
,
ds
=
None
):
if
ds
is
not
None
:
for
param
in
self
.
parameters
:
...
...
@@ -784,11 +792,12 @@ class ParticleDetectionView(QtWidgets.QWidget):
particleContainer
.
clearMeasurements
()
for
particleIndex
,
measPoints
in
enumerate
(
measurementPoints
):
for
index
,
point
in
enumerate
(
measPoints
):
curParticle
=
particleContainer
.
getParticleOfIndex
(
particleIndex
)
indexOfNewMeas
=
particleContainer
.
addEmptyMeasurement
()
particleContainer
.
setMeasurementPixelCoords
(
indexOfNewMeas
,
point
.
x
,
point
.
y
)
curParticle
.
addMeasurement
(
particleContainer
.
measurements
[
indexOfNewMeas
])
if
len
(
measPoints
)
>
0
:
for
index
,
point
in
enumerate
(
measPoints
):
curParticle
=
particleContainer
.
getParticleOfIndex
(
particleIndex
)
indexOfNewMeas
=
particleContainer
.
addEmptyMeasurement
()
particleContainer
.
setMeasurementPixelCoords
(
indexOfNewMeas
,
point
.
x
,
point
.
y
)
curParticle
.
addMeasurement
(
particleContainer
.
measurements
[
indexOfNewMeas
])
self
.
dataset
.
particleDetectionDone
=
True
self
.
dataset
.
mode
=
"prepareraman"
...
...
@@ -802,7 +811,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
try
:
stats
=
getParticleStatsWithPixelScale
(
contour
,
self
.
dataset
,
fullimage
=
self
.
img
,
zimg
=
zvalimg
)
except
InvalidParticleError
:
print
(
'
i
nvalid contour in detection, skipping particle.
Contour is:'
,
contour
)
print
(
'
I
nvalid contour in detection, skipping particle.
'
)
invalidParticleIndices
.
append
(
contourIndex
)
continue
particlestats
.
append
(
stats
)
...
...
segmentation.py
View file @
40f9393a
...
...
@@ -112,10 +112,10 @@ class Segmentation(QtCore.QObject):
Parameter
(
"activateUpThresh"
,
np
.
bool
,
self
.
detectParams
[
'activateUpThresh'
],
helptext
=
"activate upper threshold"
,
show
=
False
,
linkedParameter
=
'upThresh'
),
Parameter
(
"upThresh"
,
float
,
self
.
detectParams
[
'upThresh'
],
.
01
,
1.0
,
2
,
.
02
,
helptext
=
"Upper threshold"
,
show
=
False
),
Parameter
(
"maxholebrightness"
,
float
,
self
.
detectParams
[
'maxholebrightness'
],
0
,
1
,
2
,
0.02
,
helptext
=
"Close holes brighter than.."
,
show
=
True
),
Parameter
(
"minparticlesize"
,
int
,
self
.
detectParams
[
'minparticlesize'
],
1
,
1000
,
0
,
50
,
helptext
=
"Min. particle size (µm)"
,
show
=
False
),
Parameter
(
"minparticlesize"
,
int
,
self
.
detectParams
[
'minparticlesize'
],
1
,
1000
,
0
,
1
,
helptext
=
"Min. particle size (µm)"
,
show
=
False
),
Parameter
(
"enableMaxArea"
,
np
.
bool
,
self
.
detectParams
[
'enableMaxArea'
],
helptext
=
"enable filtering for maximal particle size"
,
show
=
False
,
linkedParameter
=
'maxparticlearea'
),
Parameter
(
"maxparticlesize"
,
int
,
self
.
detectParams
[
'maxparticlesize'
],
10
,
1E9
,
0
,
50
,
helptext
=
"Max. particle size (µm)"
,
show
=
False
),
Parameter
(
"minparticledistance"
,
int
,
self
.
detectParams
[
'minparticledistance'
],
5
,
1000
,
0
,
5
,
helptext
=
"Min. distance between particles"
,
show
=
False
),
Parameter
(
"minparticledistance"
,
int
,
self
.
detectParams
[
'minparticledistance'
],
1
,
1000
,
0
,
5
,
helptext
=
"Min. distance between particles
(µm)
"
,
show
=
False
),
Parameter
(
"measurefrac"
,
float
,
self
.
detectParams
[
'measurefrac'
],
0
,
1
,
2
,
stepsize
=
0.05
,
helptext
=
"measure fraction of particles"
,
show
=
False
),
Parameter
(
"closeBackground"
,
np
.
bool
,
self
.
detectParams
[
'closeBackground'
],
helptext
=
"close holes in sure background"
,
show
=
False
),
Parameter
(
"fuzzycluster"
,
np
.
bool
,
self
.
detectParams
[
'fuzzycluster'
],
helptext
=
'Enable Fuzzy Clustering'
,
show
=
False
),
...
...
@@ -147,6 +147,10 @@ class Segmentation(QtCore.QObject):
t0
=
time
()
self
.
detectionState
.
emit
(
'DO: setup'
)
if
self
.
globalScaleFactor
!=
1.
:
img
=
cv2
.
resize
(
img
,
None
,
fx
=
self
.
globalScaleFactor
,
fy
=
self
.
globalScaleFactor
)
print
(
'globalScaleFac = '
,
self
.
globalScaleFactor
)
gray
=
self
.
convert2Gray
(
img
)
self
.
detectionState
.
emit
(
'finished GrayScale'
)
print
(
"gray"
)
...
...
@@ -228,10 +232,12 @@ class Segmentation(QtCore.QObject):
self
.
detectionState
.
emit
(
'finished thresholding'
)
# modify thresh with seedpoints and deletepoints
for
p
in
np
.
int32
(
seedpoints
):
cv2
.
circle
(
thresh
,
tuple
([
p
[
0
],
p
[
1
]]),
int
(
p
[
2
]),
255
,
-
1
)
for
p
in
np
.
int32
(
seedpoints
):
#
x
,
y
,
radius
=
self
.
getXYRadiusFromSeedPoint
(
p
,
self
.
globalScaleFactor
)
cv2
.
circle
(
thresh
,
(
x
,
y
),
radius
,
255
,
-
1
)
for
p
in
np
.
int32
(
deletepoints
):
cv2
.
circle
(
thresh
,
tuple
([
p
[
0
],
p
[
1
]]),
int
(
p
[
2
]),
0
,
-
1
)
x
,
y
,
radius
=
self
.
getXYRadiusFromSeedPoint
(
p
,
self
.
globalScaleFactor
)
cv2
.
circle
(
thresh
,
(
x
,
y
),
radius
,
0
,
-
1
)
if
return_step
==
'maxholebrightness'
:
return
thresh
,
0
if
self
.
cancelcomputation
:
...
...
@@ -269,14 +275,19 @@ class Segmentation(QtCore.QObject):
subthresh
=
np
.
uint8
(
255
*
(
labels
[
up
:(
up
+
height
),
left
:(
left
+
width
)]
==
label
))
compMinArea
,
compMaxArea
=
minArea
,
maxArea
scaleFactor
=
1.0
if
width
>
self
.
maxComponentSize
or
height
>
self
.
maxComponentSize
:
scaleFactor
=
max
([
width
/
self
.
maxComponentSize
,
height
/
self
.
maxComponentSize
])
subthresh
=
cv2
.
resize
(
subthresh
,
None
,
fx
=
1
/
scaleFactor
,
fy
=
1
/
scaleFactor
)
scaleFactor
=
1
/
max
([
width
/
self
.
maxComponentSize
,
height
/
self
.
maxComponentSize
])
subthresh
=
cv2
.
resize
(
subthresh
,
None
,
fx
=
scaleFactor
,
fy
=
scaleFactor
)
compMinArea
*=
scaleFactor
compMaxArea
*=
scaleFactor
subdist
=
cv2
.
distanceTransform
(
subthresh
,
cv2
.
DIST_L2
,
3
)
minDistance
=
round
(
self
.
minparticledistance
/
scaleFactor
)
pixelScale
=
self
.
getPixelScaleWithComponentScaleFactor
(
dataset
,
scaleFactor
)
minDistance
=
round
(
self
.
minparticledistance
/
pixelScale
)
#converted from µm into px
sure_fg
=
self
.
getSureForeground
(
subthresh
,
subdist
,
minDistance
)
sure_bg
=
cv2
.
dilate
(
subthresh
,
np
.
ones
((
5
,
5
)),
iterations
=
1
)
if
self
.
closeBackground
:
...
...
@@ -284,15 +295,11 @@ class Segmentation(QtCore.QObject):
# modify sure_fg and sure_bg with seedpoints and deletepoints
for
p
in
np
.
int32
(
seedpoints
):
x
=
int
(
round
(
p
[
0
]
/
scaleFactor
)
-
left
)
y
=
int
(
round
(
p
[
1
]
/
scaleFactor
)
-
up
)
radius
=
int
(
round
(
p
[
2
]
/
scaleFactor
))
x
,
y
,
radius
=
self
.
getXYRadiusFromSeedPoint
(
p
,
self
.
globalScaleFactor
*
scaleFactor
,
left
,
up
)
cv2
.
circle
(
sure_fg
,
(
x
,
y
),
radius
,
1
,
-
1
)
cv2
.
circle
(
sure_bg
,
(
x
,
y
),
radius
,
1
,
-
1
)
for
p
in
np
.
int32
(
deletepoints
):
x
=
int
(
round
(
p
[
0
]
/
scaleFactor
)
-
left
)
y
=
int
(
round
(
p
[
1
]
/
scaleFactor
)
-
up
)
radius
=
int
(
round
(
p
[
2
]
/
scaleFactor
))
x
,
y
,
radius
=
self
.
getXYRadiusFromSeedPoint
(
p
,
self
.
globalScaleFactor
*
scaleFactor
,
left
,
up
)
cv2
.
circle
(
sure_fg
,
(
x
,
y
),
radius
,
1
,
-
1
)
cv2
.
circle
(
sure_bg
,
(
x
,
y
),
radius
,
1
,
-
1
)
...
...
@@ -333,32 +340,31 @@ class Segmentation(QtCore.QObject):
tmpcontours
=
[
contours
[
i
]
for
i
in
range
(
len
(
contours
))
if
hierarchy
[
0
,
i
,
3
]
<
0
]
left
=
int
(
round
(
left
/
self
.
globalScaleFactor
))
up
=
int
(
round
(
up
/
self
.
globalScaleFactor
))
for
cnt
in
tmpcontours
:
contourArea
=
cv2
.
contourArea
(
cnt
)
*
s
caleFactor
**
2
if
m
inArea
<=
contourArea
<=
m
axArea
:
contourArea
=
cv2
.
contourArea
(
cnt
)
/
(
scaleFactor
*
self
.
globalS
caleFactor
)
**
2
if
compM
inArea
<=
contourArea
<=
compM
axArea
:
tmplabel
=
markers
[
cnt
[
0
,
0
,
1
],
cnt
[
0
,
0
,
0
]]
if
tmplabel
==
0
:
continue
x0
,
x1
=
cnt
[:,
0
,
0
].
min
(),
cnt
[:,
0
,
0
].
max
()
y0
,
y1
=
cnt
[:,
0
,
1
].
min
(),
cnt
[:,
0
,
1
].
max
()
subimg
=
(
markers
[
y0
:
y1
+
1
,
x0
:
x1
+
1
]).
copy
()
subimg
[
subimg
!=
tmplabel
]
=
0
subimg
[
subimg
!=
tmplabel
]
=
0
y
,
x
=
self
.
getMeasurementPoints
(
subimg
)
if
scaleFactor
!=
1
:
x0
=
int
(
round
(
x0
*
scaleFactor
))
y0
=
int
(
round
(
y0
*
scaleFactor
))
x
=
[
int
(
round
(
subX
*
scaleFactor
))
for
subX
in
x
]
y
=
[
int
(
round
(
subY
*
scaleFactor
))
for
subY
in
y
]
for
i
in
range
(
len
(
cnt
)):
cnt
[
i
][
0
][
0
]
=
int
(
round
(
cnt
[
i
][
0
][
0
]
*
scaleFactor
))
cnt
[
i
][
0
][
1
]
=
int
(
round
(
cnt
[
i
][
0
][
1
]
*
scaleFactor
))
x0
=
int
(
round
(
x0
/
(
scaleFactor
*
self
.
globalScaleFactor
)))
y0
=
int
(
round
(
y0
/
(
scaleFactor
*
self
.
globalScaleFactor
)))
x
=
[
int
(
round
(
subX
/
(
scaleFactor
*
self
.
globalScaleFactor
)))
for
subX
in
x
]
y
=
[
int
(
round
(
subY
/
(
scaleFactor
*
self
.
globalScaleFactor
)))
for
subY
in
y
]
for
i
in
range
(
len
(
cnt
)):
cnt
[
i
][
0
][
0
]
+
=
left
cnt
[
i
][
0
][
1
]
+
=
up
cnt
[
i
][
0
][
0
]
=
int
(
round
(
cnt
[
i
][
0
][
0
]
/
(
scaleFactor
*
self
.
globalScaleFactor
))
+
left
)
cnt
[
i
][
0
][
1
]
=
int
(
round
(
cnt
[
i
][
0
][
1
]
/
(
scaleFactor
*
self
.
globalScaleFactor
))
+
up
)
finalcontours
.
append
(
cnt
)
measurementPoints
.
append
([])
...
...
@@ -372,13 +378,20 @@ class Segmentation(QtCore.QObject):
self
.
detectionState
.
emit
(
f
'DO: newVal=
{
label
}
'
)
if
return_step
==
'sure_fg'
:
if
self
.
globalScaleFactor
!=
1.
:
preview_surefg
=
cv2
.
resize
(
preview_surefg
,
None
,
fx
=
1
/
self
.
globalScaleFactor
,
fy
=
1
/
self
.
globalScaleFactor
)
preview_surebg
=
cv2
.
resize
(
preview_surebg
,
None
,
fx
=
1
/
self
.
globalScaleFactor
,
fy
=
1
/
self
.
globalScaleFactor
)
img
=
np
.
zeros_like
(
preview_surefg
)
img
[
np
.
nonzero
(
preview_surefg
)]
|=
1
img
[
np
.
nonzero
(
preview_surebg
)]
|=
2
return
img
,
1
elif
return_step
==
'watershed'
:
return
np
.
uint8
(
255
*
(
previewImage
!=
0
)),
0
img
=
np
.
uint8
(
255
*
(
previewImage
!=
0
))
if
self
.
globalScaleFactor
!=
1.
:
img
=
cv2
.
resize
(
img
,
None
,
fx
=
1
/
self
.
globalScaleFactor
,
fy
=
1
/
self
.
globalScaleFactor
)
return
img
,
0
elif
return_step
is
not
None
:
raise
NotImplementedError
(
f
"this particular return_step:
{
return_step
}
is not implemented yet"
)
...
...
@@ -386,9 +399,15 @@ class Segmentation(QtCore.QObject):
print
(
"particle detection took:"
,
time
()
-
t0
,
"seconds"
)
if
self
.
measurefrac
<
1.0
:
nMeasurementsDesired
=
int
(
np
.
round
(
self
.
measurefrac
*
len
(
measurementPoints
)))
numMeasPointsOrig
=
len
(
measurementPoints
)
nMeasurementsDesired
=
int
(
np
.
round
(
self
.
measurefrac
*
numMeasPointsOrig
))
print
(
f
'selecting
{
nMeasurementsDesired
}
of
{
len
(
measurementPoints
)
}
measuring spots'
)
measurementPoints
=
random
.
sample
(
measurementPoints
,
nMeasurementsDesired
)
measIndices
=
random
.
sample
(
list
(
np
.
arange
(
numMeasPointsOrig
)),
nMeasurementsDesired
)
for
partIndex
in
range
(
numMeasPointsOrig
):
if
partIndex
not
in
measIndices
:
measurementPoints
[
partIndex
]
=
[]
else
:
measIndices
.
remove
(
partIndex
)
total_time
=
time
()
-
t0
print
(
'segmentation took'
,
total_time
,
'seconds'
)
...
...
@@ -396,6 +415,12 @@ class Segmentation(QtCore.QObject):
self
.
detectionState
.
emit
(
f
'finished particle detection after
{
total_time
}
seconds'
)
return
measurementPoints
,
finalcontours
def
getXYRadiusFromSeedPoint
(
self
,
seedpoint
,
scaleFactor
=
1.
,
left
=
0
,
up
=
0
):
x
=
int
(
round
(
seedpoint
[
0
]
*
scaleFactor
)
-
left
)
y
=
int
(
round
(
seedpoint
[
1
]
*
scaleFactor
)
-
up
)
radius
=
int
(
round
(
seedpoint
[
2
]
*
scaleFactor
))
return
x
,
y
,
radius
def
getMinMaxParticleArea
(
self
,
dataset
):
"""
Converts specified particle sizes into particle areas that are used for filtering detection results.
...
...
@@ -403,7 +428,7 @@ class Segmentation(QtCore.QObject):
a = pi*(d/2)²
:return:
"""
pixelscale
=
dataset
.
getPixelScale
(
)
pixelscale
=
self
.
getPixelScale
WithComponentScaleFactor
(
dataset
,
self
.
globalScaleFactor
)
minRadius
=
(
self
.
minparticlesize
/
pixelscale
)
/
2
minArea
=
np
.
pi
*
minRadius
**
2
...
...
@@ -415,6 +440,10 @@ class Segmentation(QtCore.QObject):
return
minArea
,
maxArea
def
getPixelScaleWithComponentScaleFactor
(
self
,
dataset
,
componentScaleFactor
=
1.
):
pixelscale
=
dataset
.
getPixelScale
()
/
(
self
.
globalScaleFactor
*
componentScaleFactor
)
return
pixelscale
def
addToPreviewImage
(
self
,
subimg
,
up
,
left
,
previewImage
):
"""
Adds a subimage at given position to the previewimage
...
...
@@ -422,7 +451,7 @@ class Segmentation(QtCore.QObject):
"""
height
,
width
=
subimg
.
shape
[
0
],
subimg
.
shape
[
1
]
previewImage
[
up
:
up
+
height
,
left
:
left
+
width
]
+=
subimg
previewImage
=
np
.
array
(
previewImage
,
dtype
=
np
.
int
32
)
previewImage
=
np
.
array
(
previewImage
,
dtype
=
np
.
u
int
8
)
return
previewImage
...
...
@@ -617,8 +646,7 @@ class Segmentation(QtCore.QObject):
if
__name__
==
'__main__'
:
import
matplotlib.pyplot
as
plt
# img = cv2.imread('/home/brandt/Schreibtisch/Segmentation/fullimage_III.png')
seg
=
Segmentation
()
kwargs
=
{}
...
...
@@ -626,22 +654,4 @@ if __name__ == '__main__':
for
parameter
in
seg
.
parlist
:
kwargs
[
parameter
.
name
]
=
parameter
.
value
seg
.
setParameters
(
**
kwargs
)
size
=
25000
stepSize
=
2000
maxSize
=
40000
sizes
,
times
=
[],
[]
while
size
<=
maxSize
:
try
:
print
(
'newsize ='
,
size
)
img
=
cv2
.
resize
(
img
,
(
size
,
size
))
points
,
contours
,
tf
=
seg
.
apply2Image
(
img
,
np
.
array
([]),
np
.
array
([]),
1
,
None
)
sizes
.
append
(
size
)
times
.
append
(
tf
)
size
+=
stepSize
except
:
print
(
'segmentation failed at size'
,
size
)
raise
# imgSegmented = cv2.drawContours(img, contours, -1, (255, 255, 0), thickness=2)
# plt.imshow(imgSegmented)
\ No newline at end of file
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