Skip to content

Commit e1650e5

Browse files
Merge branch 'master' into ns-image-processing-class
2 parents 48a8077 + 3083a94 commit e1650e5

29 files changed

+179
-124
lines changed

.gitignore

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
*.iml
1+
# Folders
22
.gradle
3-
/local.properties
4-
/.idea
3+
.idea
54
/.idea/caches
65
/.idea/libraries
6+
# Files
7+
*.iml
8+
/local.properties
79
/.idea/modules.xml
810
/.idea/workspace.xml
911
/.idea/navEditor.xml
@@ -14,5 +16,6 @@
1416
.externalNativeBuild
1517
.cxx
1618
local.properties
17-
/app/src/androidTest/java/com/example/vehicleinfocheck
18-
/app/src/test/java/com/example/vehicleinfocheck
19+
# Test Folders (not used in this project, hence added to .gitignore)
20+
/app/src/androidTest/java/com/example/vehicleinfocheck/
21+
/app/src/test/java/com/example/vehicleinfocheck/

README.md

+78-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,83 @@
11
# VehicleInfoCheck
2-
3-
An app to scan license plate images and extract vehicle details.
2+
An Android application that scans Indian license plate images and extracts the details of the vehicle from the national vehicle registry, VAHAN.
43

54
Status: _In progress_
5+
## Description
6+
The app can capture an image using the device camera or choose an image from the device’s gallery which can be cropped as per the user’s preference.
7+
This image is then processed using OpenCV for Java. This involves:
8+
* Extracting the license plate from the image
9+
* Conversion of image to greyscale
10+
* OpenCV operations such as erosion and dilation to aid character segmentation
11+
* Conversion of image to binary
12+
* Finding the contour of each character present in the license plate
13+
* Cropping the license plate to get individual images for each character
14+
15+
The processed images are sent to the Tensorflow deep learning model which performs optical character recognition to identify the license plate number.
16+
> Presently, the deep learning model used in the project gives an incorrect prediction of the license plate number.
17+
18+
Next, the user is redirected to the page which displays the [VAHAN website](https://vahan.nic.in/nrservices/faces/user/login.xhtml), where the detected license plate number can be used to obtain details such as vehicle model, fuel type, manufacturer details, among others.
19+
## To import the project into Android Studio
20+
1. Open Android Studio, click **File --> New --> Project from Version Control**.
21+
2. Choose Git from the Version control dropdown.
22+
3. Copy the hyperlink present when you click on the Code button in the repository and paste in the URL tab.
23+
4. Click Clone.
24+
5. Sync project and run.
25+
## System requirements
26+
Android Studio – this project was built on version 4.1.0
27+
28+
Git installed on the device.
29+
30+
External library – OpenCV (see [Enabling OpenCV](https://github.com/Jyotsna-Shetty/VehicleInfoCheck#enabling-opencv))
31+
32+
RAM – preferably 8 GB and above (users with 4 GB RAM will have issues running the emulator).
33+
34+
Available disk space – 8 GB and above (IDE + Android SDK + Android Emulator).
35+
36+
Operating system:
37+
* 64-bit Microsoft Windows 8/10
38+
* MacOS 10.14 (Mojave) or higher
39+
* 64-bit Linux distribution that supports Gnome, KDE, or Unity DE; GNU C Library (glibc) 2.31 or later
40+
41+
Processor – 2nd generation Intel Core or newer.
42+
43+
Complete list of system requirements for Android Studio can be found [here.](https://developer.android.com/studio)
44+
### To run the app on an Android phone, the requirements are:
45+
Android version – 5.0 (Lollipop, API 21) and above.
46+
47+
Available storage space on device – 50 MB +
48+
## Enabling OpenCV
49+
1. Open `build.gradle` from the OpenCV library module present in the Project panel of Android Studio, check if the `compileSdkVersion` and `targetSdkVersion` match the version in your PC. If not, change it to your version.
50+
2. Go to **File --> Sync project with Gradle files**. The library should now be visible in the Android panel of Android Studio.
51+
## Folders
52+
### app
53+
This folder contains the main code and gradle file for the app, along with auto-generated .gitignore and proguard-rules files.
54+
55+
#### src
56+
Contains `main` folder (all Android Studio files and folders present in it) and two test folders (not used in this project). Inside `main`, we have:
57+
58+
***java/com/example/vehicleinfocheck***: Contains all java classes defining the function of the app.
59+
1. Activity classes: MainActivity- loading screen; ImageActivity- image selection and processing; WebActivity- Website display and function.
60+
2. WebScraper and Element classes: Contain methods for accessing and displaying the website within the app.
61+
62+
***ml***: Contains the deep learning tflite model.
63+
64+
***res***:
65+
1. `drawable`: All the pre-existing and imported drawables (images and xml) are present here.
66+
2. `layout`: Layout files for every activity of the are present here.
67+
3. `mipmap`: App icon files are present in these folders.
68+
4. `font`: The fonts added manually to the project are present here.
69+
5. `values`: Contains folders for strings, colours, dimensions and preloaded fonts.
70+
6. `xml`: File path for saving images is present here.
71+
72+
***AndroidManifest.xml***: Manifest file with all neccesary information regarding the app, such as app package name, activites, permissions the app needs and content providers.
73+
74+
***ic_launcher_custom-playstore.png***: Logo of the app.
675

7-
Presently, the app can take login credentials and can click images/take images from the device's gallery and display it.
76+
### Gradle files and .idea
77+
The gradle/wrapper folder, build.gradle (outside of app folder), gradle.properties, gradlew, gradlew.bat, settings.gradle, .idea are all gradles files generated by Android studio and are added to .gitignore since no changes are made to them.
78+
### LICENSE
79+
This project is licensed under the GNU General Public License v3.0. The file contains the complete description of the license, its permissions, limitations and conditions.
80+
## Credits
81+
Deep learning model used - https://github.com/SarthakV7/AI-based-indian-license-plate-detection.
882

9-
To run the app on Android studio:
10-
------------------------------
11-
Open Android Studio --> Checkout Project from Version Control --> Choose Git --> Paste the URL of the repository --> Clone
83+
Reference used for WebScraper class - https://github.com/Udayraj123/VehicleInfoOCR.

app/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ android {
3333

3434
dependencies {
3535
api 'com.theartofdev.edmodo:android-image-cropper:2.8.+'
36-
implementation 'androidx.appcompat:appcompat:1.2.0'
36+
implementation 'androidx.appcompat:appcompat:1.3.0'
3737
implementation 'com.google.android.material:material:1.3.0'
3838
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
3939
implementation 'androidx.appcompat:appcompat:1.2.0'

app/src/main/AndroidManifest.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99

1010
<application
1111
android:allowBackup="true"
12-
android:icon="@mipmap/ic_launcher_custom"
12+
android:icon="@mipmap/vic_app_logo"
1313
android:label="@string/app_name"
14-
android:roundIcon="@mipmap/ic_launcher_custom_round"
14+
android:roundIcon="@mipmap/vic_app_logo_round"
1515
android:supportsRtl="true"
1616
android:theme="@style/Theme.VehicleInfoCheck">
1717
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"/>
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,36 @@
1+
//This class is not used in this project as the required methods did not work
12
package com.example.vehicleinfocheck;
3+
//An element is a field present in the website: an input field, a button, etc.
24

35
import android.util.Log;
46

5-
// An element is a field present in the website: an input field, a button, etc.
6-
// This class defines all methods pertaining to elements
7+
//This class defines all methods pertaining to elements
78
public class Element {
8-
private final String elementLocator; // String to find an element in the website
9-
private final WebScraper web; // Instance of the WebScraper class
9+
private final String elementLocator; //String to find an element in the website
10+
private final WebScraper web; //Instance of the WebScraper class
1011

1112
Element (WebScraper web, String elementLocator){
1213
this.web = web;
1314
this.elementLocator = elementLocator;
1415
}
1516

1617
public void setText(String text){
17-
String t = "javascript:" + elementLocator + ".value='" + text + "';void(0);";
18-
Log.i("Logmsg",t);
19-
web.run(t);
18+
String task = "javascript:" + elementLocator + ".value='" + text + "';void(0);";
19+
Log.i("Logmsg",task);
20+
web.runAsUrl(task);
2021
}
2122

2223
public void setAttribute(String attribute,String text){
23-
String t = "javascript:" + elementLocator + "."+attribute+"='" + text + "';void(0);";
24-
Log.i("Logmsg",t);
25-
web.run(t);
24+
String task = "javascript:" + elementLocator + "."+attribute+"='" + text + "';void(0);";
25+
Log.i("Logmsg",task);
26+
web.runAsUrl(task);
2627
}
2728

2829
public void click(){
29-
web.run("javascript:" + elementLocator + ".click();void(0);");
30+
web.runAsUrl("javascript:" + elementLocator + ".click();void(0);");
3031
}
3132

3233
public String getName(){
33-
return web.run2("javascript:window.HtmlViewer.processContent(" + elementLocator + ".name);");
34+
return web.runJavascript("javascript:window.HtmlViewer.processContent(" + elementLocator + ".name);");
3435
}
35-
}
36+
}

app/src/main/java/com/example/vehicleinfocheck/ImageActivity.java

+33-51
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@
4747
import org.tensorflow.lite.Tensor;
4848
import org.tensorflow.lite.support.image.TensorImage;
4949
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer;
50-
;
51-
5250
import java.io.File;
5351
import java.io.FileOutputStream;
5452
import java.io.IOException;
@@ -70,10 +68,10 @@
7068
public class ImageActivity extends AppCompatActivity {
7169

7270
public static final int CAMERA_PERM_CODE = 101;
73-
public static final int CAMERA_REQUEST_CODE = 102; //camera request code
74-
public static final int GALLERY_REQUEST_CODE = 105; //gallery request code
75-
ImageView selectedImage; //import Imageview as selectedImage
76-
Button cameraBtn,galleryBtn, ScanBtn; //import buttons
71+
public static final int CAMERA_REQUEST_CODE = 102; //camera request code
72+
public static final int GALLERY_REQUEST_CODE = 105; //gallery request code
73+
ImageView selectedImage; //import Imageview as selectedImage
74+
Button cameraBtn,galleryBtn, ScanBtn; //import buttons
7775
TextView sampleImgText;
7876
String currentPhotoPath;
7977
//String galleryPhotoPath;
@@ -97,16 +95,15 @@ public class ImageActivity extends AppCompatActivity {
9795
protected void onCreate(Bundle savedInstanceState) {
9896
super.onCreate(savedInstanceState);
9997
setContentView(R.layout.activity_image);
100-
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); //fixes orientation to PORTRAIT mode
98+
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); //fixes orientation to PORTRAIT mode
10199

102100
selectedImage = findViewById(R.id.displayImageView); //finding imageview
103101
sampleImgText = findViewById(R.id.SampleImgMsg);
104-
cameraBtn = findViewById(R.id.cameraBtn);
102+
cameraBtn = findViewById(R.id.cameraBtn);
105103
galleryBtn = findViewById(R.id.galleryBtn);
106-
ScanBtn = findViewById(R.id.ScanBtn);
104+
ScanBtn = findViewById(R.id.ScanBtn);
107105

108106
cameraBtn.setOnClickListener(new View.OnClickListener(){
109-
//OnClickListener will be triggered when camera button is clicked
110107
//directs to askCameraPermissions to get Camera Permissions
111108
@Override
112109
public void onClick(View v){
@@ -115,10 +112,9 @@ public void onClick(View v){
115112
});
116113

117114
galleryBtn.setOnClickListener(new View.OnClickListener() {
118-
//OnClickListener will be triggered when gallery button is clicked
119115
@Override
120116
public void onClick(View v) {
121-
Intent gallery = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);//creating new intent to select photo from media storage
117+
Intent gallery = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); //intent to select photo from media storage
122118
startActivityForResult(gallery, GALLERY_REQUEST_CODE);
123119
}
124120
});
@@ -146,15 +142,13 @@ public void onClick(View v) {
146142
}
147143
});
148144
}
149-
150-
//using checkSelfPermission method of ContextCompact to check whether permission is granted or not
145+
//using checkSelfPermission method of ContextCompact checks whether permission is granted or not
151146
private void askCameraPermissions() {
152147
//checks for permission from manifest file using PackageManager.PERMISSION_GRANTED
153148
if(ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED){
154149
//permission for camera is passed within the string
155150
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.CAMERA}, CAMERA_PERM_CODE);
156151
}else {
157-
//user has given permission to the app
158152
dispatchTakePictureIntent();
159153
}
160154
}
@@ -163,16 +157,16 @@ private void askCameraPermissions() {
163157
@Override
164158
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
165159
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
166-
//checking permission by comparing request code of camera with request code passed to onRequestPermissionResult
160+
//checking permission by comparing request code of camera with request code passed to onRequestPermissionResult
167161
if (requestCode == CAMERA_PERM_CODE) {
168-
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){//if both the conditions are true then camera permission is given
162+
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ //camera permission given
169163
dispatchTakePictureIntent();
170164
} else {
171-
Toast.makeText(this, "Camera Permission is Required to Use camera.", Toast.LENGTH_SHORT).show();
165+
Toast.makeText(this, "Camera permission is required to use camera.", Toast.LENGTH_SHORT).show();
172166
}
173167
}
174168
}
175-
//onActivityResult method is used to display and save image
169+
//onActivityResult method is used to display and save image as data of this app
176170
@Override
177171
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
178172
super.onActivityResult(requestCode, resultCode, data);
@@ -207,6 +201,20 @@ protected void onActivityResult(int requestCode, int resultCode, @Nullable Inten
207201

208202
}
209203
}
204+
//for cropping the image
205+
if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
206+
CropImage.ActivityResult result = CropImage.getActivityResult(data);
207+
if (resultCode == RESULT_OK) {
208+
assert result != null;
209+
Uri resultUri = result.getUri();
210+
selectedImage.setImageURI(resultUri);
211+
} else if (resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) {
212+
assert result != null;
213+
Exception error = result.getError();
214+
Log.d("tag", "ERROR: " + error);
215+
216+
}
217+
}
210218
if (requestCode == GALLERY_REQUEST_CODE) {
211219
if (resultCode == Activity.RESULT_OK && data != null) {
212220
Uri contentUri = data.getData(); //creating content URI using intent data
@@ -218,40 +226,11 @@ protected void onActivityResult(int requestCode, int resultCode, @Nullable Inten
218226
}
219227
Log.d("tag", "Absolute Url of Image is " + Uri.fromFile(newFile)); //display absolute url of the file
220228
CropImage.activity(contentUri).start(this);
221-
/*galleryPhotoPath = getPath(contentUri);
222-
Log.d("GALLERY IMAGE","PATH; " + galleryPhotoPath);
223-
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());//creating file name using time stamp
224-
String imageFileName = "JPEG_" + timeStamp + "." + getFileExt(contentUri);
225-
Log.d("tag", "onActivityResult: Gallery Image Uri: " + imageFileName);*/
226-
//selectedImage.setImageURI(Uri.fromFile(newFile));
227229
sampleImgText.setVisibility(View.INVISIBLE);
228230
}
229-
230231
}
231232
}
232233

233-
/*public String getFileExt(Uri contentUri) {
234-
ContentResolver c = getContentResolver();
235-
MimeTypeMap mime = MimeTypeMap.getSingleton();
236-
return mime.getExtensionFromMimeType(c.getType(contentUri));
237-
}
238-
239-
public String getPath(Uri uri) {
240-
String path = null;
241-
String[] projection = {MediaStore.Images.Media.DATA};
242-
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
243-
if (cursor != null) {
244-
if (cursor.moveToFirst()) {
245-
int column_index = cursor.getColumnIndexOrThrow(projection[0]);
246-
path = cursor.getString(column_index);
247-
}
248-
cursor.close();
249-
}
250-
if (path == null) {
251-
path = "Not found";
252-
}
253-
return path;
254-
}*/
255234
public File getGalleryImage(Uri uri) throws IOException {
256235
InputStream in = getContentResolver().openInputStream(uri);
257236
File photoFile = createImageFile();
@@ -272,7 +251,6 @@ public File getGalleryImage(Uri uri) throws IOException {
272251
private File createImageFile() throws IOException {
273252
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); //creates time stamp
274253
String imageFileName = ";JPEG_" + timeStamp + "_"; //creating image file
275-
//Standard directory to place our image
276254
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
277255
//creating image file using the method of CreateTempFile
278256
File image = File.createTempFile(
@@ -284,6 +262,10 @@ private File createImageFile() throws IOException {
284262
currentPhotoPath = image.getAbsolutePath();//using AbsolutePath of image ,image can be displayed in imageview
285263
return image;
286264
}
265+
currentPhotoPath = image.getAbsolutePath();
266+
return image;
267+
}
268+
287269
//this method opens the camera and save our image file into the directory
288270
private void dispatchTakePictureIntent() {
289271
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
@@ -295,6 +277,7 @@ private void dispatchTakePictureIntent() {
295277
try {
296278
photoFile = createImageFile(); //this returns image
297279
} catch (IOException ex) {
280+
System.out.println(ex.getMessage());
298281
}
299282
//Continue only if the File was successfully created
300283
if (photoFile != null) {
@@ -519,5 +502,4 @@ public void setCharacterMap(){
519502
}
520503
Log.d("SET CHAR MAP","WORKS");
521504
}
522-
523-
}
505+
}

0 commit comments

Comments
 (0)