feat: initiate ocr tech in upload ktp

This commit is contained in:
pahmiudahgede 2025-05-23 02:59:57 +07:00
parent a832485e86
commit 31bbcabf16
3 changed files with 112 additions and 56 deletions

View File

@ -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,
),
], ],
), ),
), ),

View File

@ -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"

View File

@ -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