feat: initiate ocr tech in upload ktp
This commit is contained in:
parent
a832485e86
commit
31bbcabf16
|
@ -2,6 +2,8 @@ import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
|
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:image/image.dart' as img;
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:rijig_mobile/core/utils/guide.dart';
|
import 'package:rijig_mobile/core/utils/guide.dart';
|
||||||
import 'package:rijig_mobile/widget/buttoncard.dart';
|
import 'package:rijig_mobile/widget/buttoncard.dart';
|
||||||
|
|
||||||
|
@ -14,51 +16,110 @@ class UploadKtpScreen extends StatefulWidget {
|
||||||
|
|
||||||
class _UploadKtpScreenState extends State<UploadKtpScreen> {
|
class _UploadKtpScreenState extends State<UploadKtpScreen> {
|
||||||
File? _ktpImage;
|
File? _ktpImage;
|
||||||
String nik = '';
|
|
||||||
String nama = '';
|
|
||||||
String alamat = '';
|
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
|
|
||||||
|
final Map<String, TextEditingController> controllers = {
|
||||||
|
"NIK": TextEditingController(),
|
||||||
|
"Nama": TextEditingController(),
|
||||||
|
"Tempat/Tgl Lahir": TextEditingController(),
|
||||||
|
"Alamat": TextEditingController(),
|
||||||
|
"RT/RW": TextEditingController(),
|
||||||
|
"Kel/Desa": TextEditingController(),
|
||||||
|
"Kecamatan": TextEditingController(),
|
||||||
|
"Agama": TextEditingController(),
|
||||||
|
"Status Perkawinan": TextEditingController(),
|
||||||
|
"Pekerjaan": TextEditingController(),
|
||||||
|
"Kewarganegaraan": TextEditingController(),
|
||||||
|
"Berlaku Hingga": TextEditingController(),
|
||||||
|
};
|
||||||
|
|
||||||
|
final Map<String, List<String>> labelSynonyms = {
|
||||||
|
"NIK": ["nik"],
|
||||||
|
"Nama": ["nama", "nama lengkap"],
|
||||||
|
"Tempat/Tgl Lahir": ["tempat/tgl lahir", "tempat lahir", "tgl lahir"],
|
||||||
|
"Alamat": ["alamat"],
|
||||||
|
"RT/RW": ["rt/rw", "rtrw", "rt rw"],
|
||||||
|
"Kel/Desa": ["kelurahan", "desa", "kel/desa"],
|
||||||
|
"Kecamatan": ["kecamatan"],
|
||||||
|
"Agama": ["agama"],
|
||||||
|
"Status Perkawinan": ["status", "status perkawinan", "perkawinan"],
|
||||||
|
"Pekerjaan": ["pekerjaan"],
|
||||||
|
"Kewarganegaraan": ["kewarganegaraan", "warga negara"],
|
||||||
|
"Berlaku Hingga": ["berlaku", "berlaku hingga"],
|
||||||
|
};
|
||||||
|
|
||||||
Future<void> _pickImage() async {
|
Future<void> _pickImage() async {
|
||||||
final pickedFile = await ImagePicker().pickImage(source: ImageSource.camera);
|
final pickedFile = await ImagePicker().pickImage(
|
||||||
|
source: ImageSource.camera,
|
||||||
|
);
|
||||||
if (pickedFile != null) {
|
if (pickedFile != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
_ktpImage = File(pickedFile.path);
|
_ktpImage = File(pickedFile.path);
|
||||||
});
|
});
|
||||||
await _processImage(File(pickedFile.path));
|
await _processImageWithEnhancements(File(pickedFile.path));
|
||||||
setState(() => isLoading = false);
|
setState(() => isLoading = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//dari kode ini
|
|
||||||
|
|
||||||
Future<void> _processImage(File imageFile) async {
|
Future<void> _processImageWithEnhancements(File imageFile) async {
|
||||||
final inputImage = InputImage.fromFile(imageFile);
|
final rawBytes = await imageFile.readAsBytes();
|
||||||
|
final originalImage = img.decodeImage(rawBytes);
|
||||||
|
if (originalImage == null) return;
|
||||||
|
|
||||||
|
// Enhance image: grayscale + auto rotate + thresholding
|
||||||
|
var processedImage = img.grayscale(originalImage);
|
||||||
|
if (processedImage.width > processedImage.height) {
|
||||||
|
processedImage = img.copyRotate(processedImage, angle: -90);
|
||||||
|
}
|
||||||
|
// processedImage = img.threshold(processedImage, threshold: 128);
|
||||||
|
|
||||||
|
final tempDir = await getTemporaryDirectory();
|
||||||
|
final enhancedPath = '${tempDir.path}/enhanced_ktp.jpg';
|
||||||
|
final enhancedFile = File(enhancedPath)
|
||||||
|
..writeAsBytesSync(img.encodeJpg(processedImage));
|
||||||
|
|
||||||
|
final inputImage = InputImage.fromFile(enhancedFile);
|
||||||
final textRecognizer = TextRecognizer(script: TextRecognitionScript.latin);
|
final textRecognizer = TextRecognizer(script: TextRecognitionScript.latin);
|
||||||
final RecognizedText recognizedText = await textRecognizer.processImage(inputImage);
|
final RecognizedText recognizedText = await textRecognizer.processImage(
|
||||||
|
inputImage,
|
||||||
|
);
|
||||||
await textRecognizer.close();
|
await textRecognizer.close();
|
||||||
|
|
||||||
final text = recognizedText.text;
|
final lines = recognizedText.text.split('\n');
|
||||||
|
debugPrint("[OCR Result]\n${recognizedText.text}");
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
nik = _extractValue("NIK", text);
|
for (var key in controllers.keys) {
|
||||||
nama = _extractValue("Nama", text);
|
final value = _extractMultilineValue(key, lines);
|
||||||
alamat = _extractValue("Alamat", text);
|
controllers[key]?.text = value;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// terdapat error 'textRecognizer' is deprecated and shouldn't be used. Use [google_mlkit_text_recognition] plugin instead of [google_ml_kit].
|
String _extractMultilineValue(String key, List<String> lines) {
|
||||||
// Try replacing the use of the deprecated member with the replacement.dartdeprecated_member_use
|
final List<String> keywords = labelSynonyms[key] ?? [key];
|
||||||
// (deprecated) TextRecognizer textRecognizer({dynamic script = TextRecognitionScript.latin})
|
for (int i = 0; i < lines.length; i++) {
|
||||||
// Type: TextRecognizer Function({dynamic script})
|
final line = lines[i].toLowerCase();
|
||||||
|
for (final kw in keywords) {
|
||||||
|
if (line.contains(kw.toLowerCase())) {
|
||||||
|
if (lines[i].contains(":")) {
|
||||||
|
return lines[i].split(":").last.trim();
|
||||||
|
} else if (i + 1 < lines.length) {
|
||||||
|
return lines[i + 1].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
// package:google_ml_kit/src/vision.dart
|
@override
|
||||||
|
void dispose() {
|
||||||
// Return an instance of [TextRecognizer].
|
for (final controller in controllers.values) {
|
||||||
|
controller.dispose();
|
||||||
String _extractValue(String key, String fullText) {
|
}
|
||||||
final regex = RegExp('${key}s*[:s]?s*(.*)', caseSensitive: false);
|
super.dispose();
|
||||||
final match = regex.firstMatch(fullText);
|
|
||||||
return match?.group(1)?.split("\n").first.trim() ?? '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -79,40 +140,33 @@ class _UploadKtpScreenState extends State<UploadKtpScreen> {
|
||||||
_ktpImage != null
|
_ktpImage != null
|
||||||
? Image.file(_ktpImage!, height: 200)
|
? Image.file(_ktpImage!, height: 200)
|
||||||
: Container(
|
: Container(
|
||||||
height: 200,
|
height: 200,
|
||||||
color: Colors.grey.shade300,
|
color: Colors.grey.shade300,
|
||||||
child: const Center(child: Text("Belum ada gambar KTP")),
|
child: const Center(child: Text("Belum ada gambar KTP")),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
isLoading
|
isLoading
|
||||||
? const CircularProgressIndicator()
|
? const CircularProgressIndicator()
|
||||||
: CardButtonOne(
|
: CardButtonOne(
|
||||||
textButton: "Upload Foto KTP",
|
textButton: "Upload Foto KTP",
|
||||||
fontSized: 16,
|
fontSized: 16,
|
||||||
colorText: whiteColor,
|
colorText: whiteColor,
|
||||||
color: primaryColor,
|
color: primaryColor,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
horizontal: double.infinity,
|
horizontal: double.infinity,
|
||||||
vertical: 50,
|
vertical: 50,
|
||||||
onTap: _pickImage,
|
onTap: _pickImage,
|
||||||
usingRow: false,
|
usingRow: false,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
TextField(
|
for (var key in controllers.keys)
|
||||||
decoration: const InputDecoration(labelText: 'NIK'),
|
Padding(
|
||||||
controller: TextEditingController(text: nik),
|
padding: const EdgeInsets.only(bottom: 12),
|
||||||
onChanged: (val) => nik = val,
|
child: TextField(
|
||||||
),
|
decoration: InputDecoration(labelText: key),
|
||||||
TextField(
|
controller: controllers[key],
|
||||||
decoration: const InputDecoration(labelText: 'Nama'),
|
),
|
||||||
controller: TextEditingController(text: nama),
|
),
|
||||||
onChanged: (val) => nama = val,
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(labelText: 'Alamat'),
|
|
||||||
controller: TextEditingController(text: alamat),
|
|
||||||
onChanged: (val) => alamat = val,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -425,7 +425,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
image:
|
image:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
|
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
|
||||||
|
@ -641,7 +641,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||||
|
|
|
@ -31,10 +31,12 @@ dependencies:
|
||||||
http: ^1.3.0
|
http: ^1.3.0
|
||||||
http_parser: ^4.1.2
|
http_parser: ^4.1.2
|
||||||
iconsax_flutter: ^1.0.0
|
iconsax_flutter: ^1.0.0
|
||||||
|
image: ^4.5.4
|
||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
intl: ^0.20.2
|
intl: ^0.20.2
|
||||||
jwt_decoder: ^2.0.1
|
jwt_decoder: ^2.0.1
|
||||||
localstorage: ^6.0.0
|
localstorage: ^6.0.0
|
||||||
|
path_provider: ^2.1.5
|
||||||
pin_code_fields: ^8.0.1
|
pin_code_fields: ^8.0.1
|
||||||
provider: ^6.1.4
|
provider: ^6.1.4
|
||||||
shared_preferences: ^2.3.3
|
shared_preferences: ^2.3.3
|
||||||
|
|
Loading…
Reference in New Issue