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:google_mlkit_text_recognition/google_mlkit_text_recognition.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/widget/buttoncard.dart';
@ -14,51 +16,110 @@ class UploadKtpScreen extends StatefulWidget {
class _UploadKtpScreenState extends State<UploadKtpScreen> {
File? _ktpImage;
String nik = '';
String nama = '';
String alamat = '';
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 {
final pickedFile = await ImagePicker().pickImage(source: ImageSource.camera);
final pickedFile = await ImagePicker().pickImage(
source: ImageSource.camera,
);
if (pickedFile != null) {
setState(() {
isLoading = true;
_ktpImage = File(pickedFile.path);
});
await _processImage(File(pickedFile.path));
await _processImageWithEnhancements(File(pickedFile.path));
setState(() => isLoading = false);
}
}
//dari kode ini
Future<void> _processImage(File imageFile) async {
final inputImage = InputImage.fromFile(imageFile);
Future<void> _processImageWithEnhancements(File imageFile) async {
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 RecognizedText recognizedText = await textRecognizer.processImage(inputImage);
final RecognizedText recognizedText = await textRecognizer.processImage(
inputImage,
);
await textRecognizer.close();
final text = recognizedText.text;
final lines = recognizedText.text.split('\n');
debugPrint("[OCR Result]\n${recognizedText.text}");
setState(() {
nik = _extractValue("NIK", text);
nama = _extractValue("Nama", text);
alamat = _extractValue("Alamat", text);
for (var key in controllers.keys) {
final value = _extractMultilineValue(key, lines);
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].
// Try replacing the use of the deprecated member with the replacement.dartdeprecated_member_use
// (deprecated) TextRecognizer textRecognizer({dynamic script = TextRecognitionScript.latin})
// Type: TextRecognizer Function({dynamic script})
String _extractMultilineValue(String key, List<String> lines) {
final List<String> keywords = labelSynonyms[key] ?? [key];
for (int i = 0; i < lines.length; i++) {
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
// Return an instance of [TextRecognizer].
String _extractValue(String key, String fullText) {
final regex = RegExp('${key}s*[:s]?s*(.*)', caseSensitive: false);
final match = regex.firstMatch(fullText);
return match?.group(1)?.split("\n").first.trim() ?? '';
@override
void dispose() {
for (final controller in controllers.values) {
controller.dispose();
}
super.dispose();
}
@override
@ -79,40 +140,33 @@ class _UploadKtpScreenState extends State<UploadKtpScreen> {
_ktpImage != null
? Image.file(_ktpImage!, height: 200)
: Container(
height: 200,
color: Colors.grey.shade300,
child: const Center(child: Text("Belum ada gambar KTP")),
),
height: 200,
color: Colors.grey.shade300,
child: const Center(child: Text("Belum ada gambar KTP")),
),
const SizedBox(height: 20),
isLoading
? const CircularProgressIndicator()
: CardButtonOne(
textButton: "Upload Foto KTP",
fontSized: 16,
colorText: whiteColor,
color: primaryColor,
borderRadius: 10,
horizontal: double.infinity,
vertical: 50,
onTap: _pickImage,
usingRow: false,
),
textButton: "Upload Foto KTP",
fontSized: 16,
colorText: whiteColor,
color: primaryColor,
borderRadius: 10,
horizontal: double.infinity,
vertical: 50,
onTap: _pickImage,
usingRow: false,
),
const SizedBox(height: 30),
TextField(
decoration: const InputDecoration(labelText: 'NIK'),
controller: TextEditingController(text: nik),
onChanged: (val) => nik = val,
),
TextField(
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,
),
for (var key in controllers.keys)
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: TextField(
decoration: InputDecoration(labelText: key),
controller: controllers[key],
),
),
],
),
),

View File

@ -425,7 +425,7 @@ packages:
source: hosted
version: "1.0.0"
image:
dependency: transitive
dependency: "direct main"
description:
name: image
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
@ -641,7 +641,7 @@ packages:
source: hosted
version: "1.1.0"
path_provider:
dependency: transitive
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"

View File

@ -31,10 +31,12 @@ dependencies:
http: ^1.3.0
http_parser: ^4.1.2
iconsax_flutter: ^1.0.0
image: ^4.5.4
image_picker: ^1.1.2
intl: ^0.20.2
jwt_decoder: ^2.0.1
localstorage: ^6.0.0
path_provider: ^2.1.5
pin_code_fields: ^8.0.1
provider: ^6.1.4
shared_preferences: ^2.3.3