Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Augmented Reality Chemistry Laboratory
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
2021-189
Augmented Reality Chemistry Laboratory
Commits
9e7302b2
Commit
9e7302b2
authored
Jun 26, 2021
by
Charuka_IT18114454
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
image-proccesing-add tesstract , train dataste fixed issues-2
parent
599bb2c6
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
361 additions
and
4 deletions
+361
-4
app/build.gradle
app/build.gradle
+25
-4
app/src/main/AndroidManifest.xml
app/src/main/AndroidManifest.xml
+8
-0
app/src/main/assets/eng.traineddata
app/src/main/assets/eng.traineddata
+0
-0
app/src/main/assets/trainedData/eng.traineddata
app/src/main/assets/trainedData/eng.traineddata
+0
-0
app/src/main/java/com/example/archemistrylab/ImageProcessing/ImageProcess.java
.../example/archemistrylab/ImageProcessing/ImageProcess.java
+229
-0
app/src/main/java/com/example/archemistrylab/MainActivity.java
...rc/main/java/com/example/archemistrylab/MainActivity.java
+16
-0
app/src/main/python/preprosessgreyscale.py
app/src/main/python/preprosessgreyscale.py
+17
-0
app/src/main/res/layout/activity_image_process.xml
app/src/main/res/layout/activity_image_process.xml
+54
-0
app/src/main/res/layout/content_main.xml
app/src/main/res/layout/content_main.xml
+12
-0
No files found.
app/build.gradle
View file @
9e7302b2
...
@@ -8,11 +8,11 @@ apply plugin: 'com.chaquo.python'
...
@@ -8,11 +8,11 @@ apply plugin: 'com.chaquo.python'
android
{
android
{
compileSdkVersion
30
compileSdkVersion
30
buildToolsVersion
"30.0.
2
"
buildToolsVersion
"30.0.
3
"
defaultConfig
{
defaultConfig
{
applicationId
"com.example.archemistrylab"
applicationId
"com.example.archemistrylab"
minSdkVersion
19
minSdkVersion
23
targetSdkVersion
30
targetSdkVersion
30
versionCode
1
versionCode
1
versionName
"1.0"
versionName
"1.0"
...
@@ -23,9 +23,20 @@ android {
...
@@ -23,9 +23,20 @@ android {
abiFilters
"armeabi-v7a"
,
"x86"
abiFilters
"armeabi-v7a"
,
"x86"
}
}
python
{
python
{
buildPython
"C:/Users/Mahima/AppData/Local/Programs/Python/Python36/python.exe"
buildPython
"C:/Users/User/AppData/Local/Programs/Python/Python38-32/python.exe"
pip
{
install
"pillow"
install
"numpy"
install
"opencv-contrib-python"
install
"pytesseract"
install
"matplotlib"
}
}
}
sourceSets
{
sourceSets
{
main
{
main
{
python
.
srcDir
"src/main/python"
python
.
srcDir
"src/main/python"
...
@@ -43,17 +54,27 @@ android {
...
@@ -43,17 +54,27 @@ android {
sourceCompatibility
JavaVersion
.
VERSION_1_8
sourceCompatibility
JavaVersion
.
VERSION_1_8
targetCompatibility
JavaVersion
.
VERSION_1_8
targetCompatibility
JavaVersion
.
VERSION_1_8
}
}
sourceSets
{
main
{
assets
{
srcDirs
'src\\main\\assets'
}
}
}
}
}
dependencies
{
dependencies
{
implementation
'androidx.appcompat:appcompat:1.
2
.0'
implementation
'androidx.appcompat:appcompat:1.
3
.0'
implementation
'com.google.android.material:material:1.3.0'
implementation
'com.google.android.material:material:1.3.0'
implementation
'androidx.constraintlayout:constraintlayout:2.0.4'
implementation
'androidx.constraintlayout:constraintlayout:2.0.4'
implementation
'androidx.navigation:navigation-fragment:2.3.5'
implementation
'androidx.navigation:navigation-fragment:2.3.5'
implementation
'androidx.navigation:navigation-ui:2.3.5'
implementation
'androidx.navigation:navigation-ui:2.3.5'
implementation
'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation
'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation
'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation
'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation
'com.theartofdev.edmodo:android-image-cropper:2.6.0'
implementation
'com.rmtheis:tess-two:5.4.1'
testImplementation
'junit:junit:4.13.2'
testImplementation
'junit:junit:4.13.2'
androidTestImplementation
'androidx.test.ext:junit:1.1.2'
androidTestImplementation
'androidx.test.ext:junit:1.1.2'
androidTestImplementation
'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation
'androidx.test.espresso:espresso-core:3.3.0'
...
...
app/src/main/AndroidManifest.xml
View file @
9e7302b2
...
@@ -2,6 +2,10 @@
...
@@ -2,6 +2,10 @@
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
package=
"com.example.archemistrylab"
>
package=
"com.example.archemistrylab"
>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.CAMERA"
/>
<application
<application
android:allowBackup=
"true"
android:allowBackup=
"true"
android:icon=
"@mipmap/ic_launcher"
android:icon=
"@mipmap/ic_launcher"
...
@@ -9,6 +13,10 @@
...
@@ -9,6 +13,10 @@
android:roundIcon=
"@mipmap/ic_launcher_round"
android:roundIcon=
"@mipmap/ic_launcher_round"
android:supportsRtl=
"true"
android:supportsRtl=
"true"
android:theme=
"@style/Theme.ARChemistryLab"
>
android:theme=
"@style/Theme.ARChemistryLab"
>
<activity
android:name=
".ImageProcessing.ImageProcess"
></activity>
<activity
android:name=
"com.theartofdev.edmodo.cropper.CropImageActivity"
android:theme=
"@style/Theme.AppCompat.DayNight.DarkActionBar"
/>
<activity
<activity
android:name=
".MainActivity"
android:name=
".MainActivity"
android:label=
"@string/app_name"
android:label=
"@string/app_name"
...
...
app/src/main/assets/eng.traineddata
0 → 100644
View file @
9e7302b2
File added
app/src/main/assets/trainedData/eng.traineddata
0 → 100644
View file @
9e7302b2
File added
app/src/main/java/com/example/archemistrylab/ImageProcessing/ImageProcess.java
0 → 100644
View file @
9e7302b2
package
com.example.archemistrylab.ImageProcessing
;
import
android.Manifest
;
import
android.content.Intent
;
import
android.content.pm.PackageManager
;
import
android.graphics.Bitmap
;
import
android.graphics.BitmapFactory
;
import
android.net.Uri
;
import
android.os.Bundle
;
import
android.os.Environment
;
import
android.provider.MediaStore
;
import
android.util.Base64
;
import
android.view.View
;
import
android.widget.Button
;
import
android.widget.ImageView
;
import
android.widget.TextView
;
import
android.widget.Toast
;
import
androidx.annotation.Nullable
;
import
androidx.appcompat.app.AppCompatActivity
;
import
androidx.core.app.ActivityCompat
;
import
com.chaquo.python.PyObject
;
import
com.chaquo.python.Python
;
import
com.chaquo.python.android.AndroidPlatform
;
import
com.example.archemistrylab.R
;
import
com.googlecode.tesseract.android.TessBaseAPI
;
import
com.theartofdev.edmodo.cropper.CropImage
;
import
java.io.ByteArrayOutputStream
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
public
class
ImageProcess
extends
AppCompatActivity
{
ImageView
originalimage
,
imageviewUpdate
;
Button
btnCapture
;
Uri
uri
;
Bitmap
finalbitmap
;
TextView
textfinal
;
String
FinalDatapath
;
String
FinalPathToTESS_BASE_API
;
private
TessBaseAPI
tessBaseAPI
;
Python
py
=
null
;
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
setContentView
(
R
.
layout
.
activity_image_process
);
originalimage
=
findViewById
(
R
.
id
.
OriginalImageView
);
btnCapture
=
findViewById
(
R
.
id
.
capture
);
imageviewUpdate
=
findViewById
(
R
.
id
.
imageviewUpdate
);
textfinal
=
findViewById
(
R
.
id
.
textfinal
);
//start python
if
(!
Python
.
isStarted
()){
Python
.
start
(
new
AndroidPlatform
(
this
));
}
// creating python instance
py
=
Python
.
getInstance
();
btnCapture
.
setOnClickListener
(
new
View
.
OnClickListener
()
{
@Override
public
void
onClick
(
View
v
)
{
CropImage
.
activity
().
start
(
com
.
example
.
archemistrylab
.
ImageProcessing
.
ImageProcess
.
this
);
}
});
}
@Override
protected
void
onActivityResult
(
int
requestCode
,
int
resultCode
,
@Nullable
Intent
data
)
{
super
.
onActivityResult
(
requestCode
,
resultCode
,
data
);
if
(
requestCode
==
CropImage
.
CROP_IMAGE_ACTIVITY_REQUEST_CODE
){
CropImage
.
ActivityResult
result
=
CropImage
.
getActivityResult
(
data
);
if
(
resultCode
==
RESULT_OK
){
uri
=
result
.
getUri
();
Bitmap
bitmap
=
null
;
try
{
bitmap
=
MediaStore
.
Images
.
Media
.
getBitmap
(
this
.
getContentResolver
(),
uri
);
finalbitmap
=
bitmap
;
originalimage
.
setImageBitmap
(
bitmap
);
}
catch
(
IOException
e
){
e
.
printStackTrace
();
}
}
else
if
(
resultCode
==
CropImage
.
CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE
){
Exception
e
=
result
.
getError
();
Toast
.
makeText
(
this
,
"Possible Error is : "
+
e
,
Toast
.
LENGTH_LONG
).
show
();
}
}
//Check the permissions before accessing the external storage
if
(
ActivityCompat
.
checkSelfPermission
(
com
.
example
.
archemistrylab
.
ImageProcessing
.
ImageProcess
.
this
,
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
)
==
PackageManager
.
PERMISSION_GRANTED
){
if
(
ActivityCompat
.
checkSelfPermission
(
com
.
example
.
archemistrylab
.
ImageProcessing
.
ImageProcess
.
this
,
Manifest
.
permission
.
READ_EXTERNAL_STORAGE
)
==
PackageManager
.
PERMISSION_GRANTED
){
PrepareTessData
();
startOCR
(
finalbitmap
);
}
else
{
ActivityCompat
.
requestPermissions
(
com
.
example
.
archemistrylab
.
ImageProcessing
.
ImageProcess
.
this
,
new
String
[]{
Manifest
.
permission
.
READ_EXTERNAL_STORAGE
},
101
);
}
}
else
{
//when permission is not granted
//Request Permission
ActivityCompat
.
requestPermissions
(
com
.
example
.
archemistrylab
.
ImageProcessing
.
ImageProcess
.
this
,
new
String
[]{
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
},
100
);
}
}
//prepare the data for Tesseract
private
void
PrepareTessData
(){
try
{
String
fileList
[]
=
getAssets
().
list
(
"trainedData"
);
for
(
String
fileName
:
fileList
)
{
File
file
=
new
File
(
Environment
.
getExternalStorageDirectory
()
+
"/tesseract/tessdata/"
);
if
(!
file
.
exists
()){
file
.
mkdirs
();
}
if
((
file
.
exists
()))
{
try
{
InputStream
in
=
getAssets
().
open
(
fileName
);
String
sdCardPath
=
Environment
.
getExternalStorageDirectory
()
+
"/tesseract/tessdata"
;
FinalDatapath
=
sdCardPath
+
"/eng.traineddata"
;
FinalPathToTESS_BASE_API
=
Environment
.
getExternalStorageDirectory
()
+
"/tesseract/"
;
System
.
out
.
println
(
"sdpath "
+
sdCardPath
);
OutputStream
out
=
new
FileOutputStream
(
FinalDatapath
);
byte
[]
buff
=
new
byte
[
1024
];
int
len
;
while
((
len
=
in
.
read
(
buff
))
>
0
){
out
.
write
(
buff
,
0
,
len
);
}
in
.
close
();
out
.
close
();
}
catch
(
IOException
e
){
e
.
printStackTrace
();
}
}
}
}
catch
(
Exception
e
){
e
.
printStackTrace
();
}
}
private
void
startOCR
(
Bitmap
bitmap
){
try
{
BitmapFactory
.
Options
options
=
new
BitmapFactory
.
Options
();
options
.
inSampleSize
=
7
;
ByteArrayOutputStream
baos
=
new
ByteArrayOutputStream
();
bitmap
.
compress
(
Bitmap
.
CompressFormat
.
PNG
,
100
,
baos
);
//store image data in byte array
byte
[]
imageBytes
=
baos
.
toByteArray
();
//Encode the data to string
String
encodedImage
=
Base64
.
encodeToString
(
imageBytes
,
Base64
.
DEFAULT
);
PyObject
pyo
=
py
.
getModule
(
"preprosessgreyscale"
);
//open the python script
PyObject
obj
=
pyo
.
callAttr
(
"main"
,
encodedImage
);
//getting the image String from obj
String
imgStr
=
obj
.
toString
();
//convert again to byte array
byte
data
[]
=
Base64
.
decode
(
imgStr
,
Base64
.
DEFAULT
);
// convert byte array to bitmap
Bitmap
greybmp
=
BitmapFactory
.
decodeByteArray
(
data
,
0
,
data
.
length
);
imageviewUpdate
.
setImageBitmap
(
greybmp
);
String
textfromImage
=
getOCRresult
(
greybmp
,
FinalPathToTESS_BASE_API
);
textfinal
.
setText
(
textfromImage
);
}
catch
(
Exception
e
){
e
.
printStackTrace
();
}
}
public
String
getOCRresult
(
Bitmap
bitmap
,
String
FinalPathToTESS_BASE_API
){
try
{
tessBaseAPI
=
new
TessBaseAPI
();
}
catch
(
Exception
e
){
e
.
printStackTrace
();
}
tessBaseAPI
.
init
(
FinalPathToTESS_BASE_API
,
"eng"
);
tessBaseAPI
.
setVariable
(
TessBaseAPI
.
VAR_CHAR_WHITELIST
,
"aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ1234567890',.?;/=>+ "
);
tessBaseAPI
.
setImage
(
bitmap
);
String
retStr
=
"No Result"
;
try
{
retStr
=
tessBaseAPI
.
getUTF8Text
();
}
catch
(
Exception
e
){
e
.
printStackTrace
();
}
tessBaseAPI
.
end
();
return
retStr
;
}
}
\ No newline at end of file
app/src/main/java/com/example/archemistrylab/MainActivity.java
View file @
9e7302b2
package
com.example.archemistrylab
;
package
com.example.archemistrylab
;
import
android.content.Intent
;
import
android.os.Bundle
;
import
android.os.Bundle
;
import
android.view.View
;
import
android.view.View
;
import
android.view.Menu
;
import
android.view.Menu
;
import
android.widget.Button
;
import
android.widget.TextView
;
import
android.widget.TextView
;
import
com.google.android.material.floatingactionbutton.FloatingActionButton
;
import
com.google.android.material.floatingactionbutton.FloatingActionButton
;
...
@@ -21,11 +23,17 @@ import com.chaquo.python.PyObject;
...
@@ -21,11 +23,17 @@ import com.chaquo.python.PyObject;
import
com.chaquo.python.Python
;
import
com.chaquo.python.Python
;
import
com.chaquo.python.android.AndroidPlatform
;
import
com.chaquo.python.android.AndroidPlatform
;
import
com.example.archemistrylab.ImageProcessing.ImageProcess
;
public
class
MainActivity
extends
AppCompatActivity
{
public
class
MainActivity
extends
AppCompatActivity
{
private
AppBarConfiguration
mAppBarConfiguration
;
private
AppBarConfiguration
mAppBarConfiguration
;
TextView
textView
;
TextView
textView
;
String
Imagestring
=
""
;
Python
py
;
Button
buttonimagepro
;
@Override
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
super
.
onCreate
(
savedInstanceState
);
...
@@ -34,6 +42,7 @@ public class MainActivity extends AppCompatActivity {
...
@@ -34,6 +42,7 @@ public class MainActivity extends AppCompatActivity {
setSupportActionBar
(
toolbar
);
setSupportActionBar
(
toolbar
);
FloatingActionButton
fab
=
findViewById
(
R
.
id
.
fab
);
FloatingActionButton
fab
=
findViewById
(
R
.
id
.
fab
);
textView
=
(
TextView
)
findViewById
(
R
.
id
.
textview
);
textView
=
(
TextView
)
findViewById
(
R
.
id
.
textview
);
buttonimagepro
=
findViewById
(
R
.
id
.
buttonimagepro
);
if
(!
Python
.
isStarted
())
{
if
(!
Python
.
isStarted
())
{
Python
.
start
(
new
AndroidPlatform
(
this
));
Python
.
start
(
new
AndroidPlatform
(
this
));
}
}
...
@@ -55,6 +64,13 @@ public class MainActivity extends AppCompatActivity {
...
@@ -55,6 +64,13 @@ public class MainActivity extends AppCompatActivity {
.
setAction
(
"Action"
,
null
).
show
();
.
setAction
(
"Action"
,
null
).
show
();
}
}
});
});
buttonimagepro
.
setOnClickListener
(
new
View
.
OnClickListener
()
{
@Override
public
void
onClick
(
View
v
)
{
Intent
intent
=
new
Intent
(
MainActivity
.
this
,
ImageProcess
.
class
);
startActivity
(
intent
);
}
});
DrawerLayout
drawer
=
findViewById
(
R
.
id
.
drawer_layout
);
DrawerLayout
drawer
=
findViewById
(
R
.
id
.
drawer_layout
);
NavigationView
navigationView
=
findViewById
(
R
.
id
.
nav_view
);
NavigationView
navigationView
=
findViewById
(
R
.
id
.
nav_view
);
// Passing each menu ID as a set of Ids because each
// Passing each menu ID as a set of Ids because each
...
...
app/src/main/python/preprosessgreyscale.py
0 → 100644
View file @
9e7302b2
import
numpy
as
np
import
cv2
from
PIL
import
Image
import
base64
import
io
def
main
(
data
):
decoded_data
=
base64
.
b64decode
(
data
)
np_data
=
np
.
fromstring
(
decoded_data
,
np
.
uint8
)
img
=
cv2
.
imdecode
(
np_data
,
cv2
.
IMREAD_UNCHANGED
)
img_gray
=
cv2
.
cvtColor
(
img
,
cv2
.
COLOR_BGR2GRAY
)
pil_im
=
Image
.
fromarray
(
img_gray
)
buff
=
io
.
BytesIO
()
pil_im
.
save
(
buff
,
format
=
"PNG"
)
img_str
=
base64
.
b64encode
(
buff
.
getvalue
())
return
""
+
str
(
img_str
,
'utf-8'
)
\ No newline at end of file
app/src/main/res/layout/activity_image_process.xml
0 → 100644
View file @
9e7302b2
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
tools:context=
".ImageProcessing.ImageProcess"
>
<ImageView
android:id=
"@+id/OriginalImageView"
android:layout_width=
"193dp"
android:layout_height=
"173dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintHorizontal_bias=
"0.073"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintVertical_bias=
"0.224"
tools:srcCompat=
"@tools:sample/avatars"
/>
<Button
android:id=
"@+id/capture"
android:layout_width=
"243dp"
android:layout_height=
"66dp"
android:text=
"Button"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintVertical_bias=
"0.545"
/>
<ImageView
android:id=
"@+id/imageviewUpdate"
android:layout_width=
"169dp"
android:layout_height=
"166dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintHorizontal_bias=
"0.933"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintVertical_bias=
"0.233"
tools:srcCompat=
"@tools:sample/avatars"
/>
<TextView
android:id=
"@+id/textfinal"
android:layout_width=
"287dp"
android:layout_height=
"71dp"
android:text=
"TextView"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintVertical_bias=
"0.757"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
app/src/main/res/layout/content_main.xml
View file @
9e7302b2
...
@@ -28,5 +28,17 @@
...
@@ -28,5 +28,17 @@
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:navGraph=
"@navigation/mobile_navigation"
/>
app:navGraph=
"@navigation/mobile_navigation"
/>
<Button
android:id=
"@+id/buttonimagepro"
android:layout_width=
"184dp"
android:layout_height=
"78dp"
android:text=
"Button"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintHorizontal_bias=
"0.497"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"@+id/nav_host_fragment"
app:layout_constraintVertical_bias=
"0.808"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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