Closed talha-inozu closed 1 year ago
Image data are generally encoded in jp2 (jpeg 2000). Flutter can't decrypt this kind of format natively. You have to convert it in a readable format before using it.
Image data are generally encoded in jp2 (jpeg 2000). Flutter can't decrypt this kind of format natively. You have to convert it in a readable format before using it.
My problem is about understading the data which comes from dg2 tag, ı can convert the format there is not a problem at there but in dg2, a lot of informations are there and ı dont know how to parse it for take photo byte array
Image data are generally encoded in jp2 (jpeg 2000). Flutter can't decrypt this kind of format natively. You have to convert it in a readable format before using it.
My problem is about understading the data which comes from dg2 tag, ı can convert the format there is not a problem at there but in dg2, a lot of informations are there and ı dont know how to parse it for take photo byte array
You can find the documentation here: https://www.icao.int/publications/documents/9303_p10_cons_en.pdf
Image data are generally encoded in jp2 (jpeg 2000). Flutter can't decrypt this kind of format natively. You have to convert it in a readable format before using it.
My problem is about understading the data which comes from dg2 tag, ı can convert the format there is not a problem at there but in dg2, a lot of informations are there and ı dont know how to parse it for take photo byte array
You can find the documentation here: https://www.icao.int/publications/documents/9303_p10_cons_en.pdf
Thank you some much this what ı exactly want it. Also is there any code example for extracting images from dg2 ?
Hi @talha-inozu , imageData seems incorrect did you manage to get correct image?
how do I convert the jpeg 2000 to jpeg in flutter. Anyone been able to achieve this? @eliasto @smlu
I believe no one has made dart converter for jpeg2000. Your best bet is probably using OpenJpeg C library through FFI or create native Flutter plugin for android (JP2ForAndroid) and iOS.
I believe no one has made dart converter for jpeg2000. Your best bet is probably using OpenJpeg C library through FFI or create native Flutter plugin for android (JP2ForAndroid) and iOS.
Thank you for your suggestion! I'm interested in using the OpenJpeg C library through FFI to work with JPEG2000. However, I'm currently facing a challenge in figuring out how to pass the Uint8List
data from Dart to a C function that expects a void*
pointer for the p_stream. I tried with ffi.Pointer<Uint8>
but failed to decode it.
Could you provide more details or guidance on how to properly convert and pass the Uint8List
data as a void*
pointer in C? Any insights or code examples would be greatly appreciated.
I believe no one has made dart converter for jpeg2000. Your best bet is probably using OpenJpeg C library through FFI or create native Flutter plugin for android (JP2ForAndroid) and iOS.
Thank you for your suggestion! I'm interested in using the OpenJpeg C library through FFI to work with JPEG2000. However, I'm currently facing a challenge in figuring out how to pass the
Uint8List
data from Dart to a C function that expects avoid*
pointer for the p_stream. I tried withffi.Pointer<Uint8>
but failed to decode it.Could you provide more details or guidance on how to properly convert and pass the
Uint8List
data as avoid*
pointer in C? Any insights or code examples would be greatly appreciated.
share with me your repo and I will make PR to help
share with me your repo and I will make PR to help
Thank you for your response @Taym95. This is my repo openjpeg_ffi. I think the problem is within the C function.
im also trying to achieve this , has anyone been able to do this please
ive been able to display the images, in case someone wants to do this in future , all you need Platfom Channels heres a sample code
the imageData from dmrtd is in Uint8List after parsing, call the method channel
dart
const imageChannel = MethodChannel('image_channel');
Future<Uint8List> decodeImage(Uint8List jp2ImageData,) async {
Uint8List decodedImageData;
final result = await imageChannel
.invokeMethod('decodeImage', {'jp2ImageData': jp2ImageData});
decodedImageData = Uint8List.fromList(result);
return decodedImageData;
}
add this in a kotlin file (imageutil.kt for example) kotlin
package com.your.packagenamehere
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import io.flutter.plugin.common.MethodChannel
import com.gemalto.jp2.JP2Decoder
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
object ImageUtil {
fun decodeImage(context: Context?, jp2ImageData: ByteArray, result: MethodChannel.Result) {
// Convert JP2 ByteArray to InputStream
val inputStream: InputStream = ByteArrayInputStream(jp2ImageData)
// Use the existing decodeImage function
val decodedBitmap = decodeImage(context, "image/jp2", inputStream)
// Convert the Bitmap to a ByteArray for sending back to Flutter
val byteArrayOutputStream = ByteArrayOutputStream()
decodedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream)
val byteArray = byteArrayOutputStream.toByteArray()
// Send the decoded image back to Flutter
result.success(byteArray)
}
fun decodeImage(context: Context?, mimeType: String, inputStream: InputStream?): Bitmap {
return if (mimeType.equals("image/jp2", ignoreCase = true) || mimeType.equals(
"image/jpeg2000",
ignoreCase = true
)
) {
JP2Decoder(inputStream).decode()
} else {
// Add other decoding logic if needed
BitmapFactory.decodeStream(inputStream)
}
}
}
then in your mainActivity.kt register the method
package com.your.packagenamehere
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugin.common.MethodChannel
import android.content.Context
import com.scansolutions.mrzflutterplugin_example.ImageUtil
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "image_channel")
.setMethodCallHandler { call, result ->
if (call.method == "decodeImage") {
val jp2ImageData = call.argument<ByteArray?>("jp2ImageData")
if (jp2ImageData != null) {
ImageUtil.decodeImage(applicationContext, jp2ImageData, result)
} else {
result.error("INVALID_ARGUMENT", "jp2ImageData is null", null)
}
} else {
result.notImplemented()
}
}
}
}
then lastly in app directory gradle.build file add this dep
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// Gemalto JP2 Decoder
// https://mvnrepository.com/artifact/com.gemalto.jp2/jp2-android
implementation 'com.gemalto.jp2:jp2-android:1.0.3'
// // JNBIS (WSQ Decoder)
// implementation 'org.jnbis:jnbis:1.7'
}
decodeImage()
method call the native code which converts the imageData from jpeg2000 to jpg, returns the image data (Uint8List) and you can simply display with Image.memory(imageData)
or anything
ive been able to display the images, in case someone wants to do this in future , all you need Platfom Channels heres a sample code
the imageData from dmrtd is in Uint8List after parsing, call the method channel
dart
const imageChannel = MethodChannel('image_channel'); Future<Uint8List> decodeImage(Uint8List jp2ImageData,) async { Uint8List decodedImageData; final result = await imageChannel .invokeMethod('decodeImage', {'jp2ImageData': jp2ImageData}); decodedImageData = Uint8List.fromList(result); return decodedImageData; }
add this in a kotlin file (imageutil.kt for example) kotlin
package com.your.packagenamehere import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import io.flutter.plugin.common.MethodChannel import com.gemalto.jp2.JP2Decoder import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStream object ImageUtil { fun decodeImage(context: Context?, jp2ImageData: ByteArray, result: MethodChannel.Result) { // Convert JP2 ByteArray to InputStream val inputStream: InputStream = ByteArrayInputStream(jp2ImageData) // Use the existing decodeImage function val decodedBitmap = decodeImage(context, "image/jp2", inputStream) // Convert the Bitmap to a ByteArray for sending back to Flutter val byteArrayOutputStream = ByteArrayOutputStream() decodedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) val byteArray = byteArrayOutputStream.toByteArray() // Send the decoded image back to Flutter result.success(byteArray) } fun decodeImage(context: Context?, mimeType: String, inputStream: InputStream?): Bitmap { return if (mimeType.equals("image/jp2", ignoreCase = true) || mimeType.equals( "image/jpeg2000", ignoreCase = true ) ) { JP2Decoder(inputStream).decode() } else { // Add other decoding logic if needed BitmapFactory.decodeStream(inputStream) } } }
then in your mainActivity.kt register the method
package com.your.packagenamehere import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugins.GeneratedPluginRegistrant import io.flutter.plugin.common.MethodChannel import android.content.Context import com.scansolutions.mrzflutterplugin_example.ImageUtil class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "image_channel") .setMethodCallHandler { call, result -> if (call.method == "decodeImage") { val jp2ImageData = call.argument<ByteArray?>("jp2ImageData") if (jp2ImageData != null) { ImageUtil.decodeImage(applicationContext, jp2ImageData, result) } else { result.error("INVALID_ARGUMENT", "jp2ImageData is null", null) } } else { result.notImplemented() } } } }
then lastly in app directory gradle.build file add this dep
dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // Gemalto JP2 Decoder // https://mvnrepository.com/artifact/com.gemalto.jp2/jp2-android implementation 'com.gemalto.jp2:jp2-android:1.0.3' // // JNBIS (WSQ Decoder) // implementation 'org.jnbis:jnbis:1.7' }
decodeImage()
method call the native code which converts the imageData from jpeg2000 to jpg, returns the image data (Uint8List) and you can simply display withImage.memory(imageData)
or anything
Your Code is exactly what i need and I am extremely happy having found it, although it is not completely working for me. If i pass the DG2 MDTDS ByteArray (Uint8List) to the Platform Channel, I receive the error:
"E/OpenJPEG(11299): Unknown file format" on the method JP2Decoder(inputStream).decode()
I made sure the ByteStream is there (10160 bytes), but i always get the same error, even though i did not touch the Stream from the DG2 Tag. Did you maybe encounter the same error, or rather do you have any idea on how to fix it?
Thanks in advance!
ive been able to display the images, in case someone wants to do this in future , all you need Platfom Channels heres a sample code
the imageData from dmrtd is in Uint8List after parsing, call the method channel
dart
const imageChannel = MethodChannel('image_channel'); Future<Uint8List> decodeImage(Uint8List jp2ImageData,) async { Uint8List decodedImageData; final result = await imageChannel .invokeMethod('decodeImage', {'jp2ImageData': jp2ImageData}); decodedImageData = Uint8List.fromList(result); return decodedImageData; }
add this in a kotlin file (imageutil.kt for example) kotlin
package com.your.packagenamehere import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import io.flutter.plugin.common.MethodChannel import com.gemalto.jp2.JP2Decoder import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStream object ImageUtil { fun decodeImage(context: Context?, jp2ImageData: ByteArray, result: MethodChannel.Result) { // Convert JP2 ByteArray to InputStream val inputStream: InputStream = ByteArrayInputStream(jp2ImageData) // Use the existing decodeImage function val decodedBitmap = decodeImage(context, "image/jp2", inputStream) // Convert the Bitmap to a ByteArray for sending back to Flutter val byteArrayOutputStream = ByteArrayOutputStream() decodedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) val byteArray = byteArrayOutputStream.toByteArray() // Send the decoded image back to Flutter result.success(byteArray) } fun decodeImage(context: Context?, mimeType: String, inputStream: InputStream?): Bitmap { return if (mimeType.equals("image/jp2", ignoreCase = true) || mimeType.equals( "image/jpeg2000", ignoreCase = true ) ) { JP2Decoder(inputStream).decode() } else { // Add other decoding logic if needed BitmapFactory.decodeStream(inputStream) } } }
then in your mainActivity.kt register the method
package com.your.packagenamehere import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugins.GeneratedPluginRegistrant import io.flutter.plugin.common.MethodChannel import android.content.Context import com.scansolutions.mrzflutterplugin_example.ImageUtil class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "image_channel") .setMethodCallHandler { call, result -> if (call.method == "decodeImage") { val jp2ImageData = call.argument<ByteArray?>("jp2ImageData") if (jp2ImageData != null) { ImageUtil.decodeImage(applicationContext, jp2ImageData, result) } else { result.error("INVALID_ARGUMENT", "jp2ImageData is null", null) } } else { result.notImplemented() } } } }
then lastly in app directory gradle.build file add this dep
dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // Gemalto JP2 Decoder // https://mvnrepository.com/artifact/com.gemalto.jp2/jp2-android implementation 'com.gemalto.jp2:jp2-android:1.0.3' // // JNBIS (WSQ Decoder) // implementation 'org.jnbis:jnbis:1.7' }
decodeImage()
method call the native code which converts the imageData from jpeg2000 to jpg, returns the image data (Uint8List) and you can simply display withImage.memory(imageData)
or anythingYour Code is exactly what i need and I am extremely happy having found it, although it is not completely working for me. If i pass the DG2 MDTDS ByteArray (Uint8List) to the Platform Channel, I receive the error:
"E/OpenJPEG(11299): Unknown file format" on the method JP2Decoder(inputStream).decode()
I made sure the ByteStream is there (10160 bytes), but i always get the same error, even though i did not touch the Stream from the DG2 Tag. Did you maybe encounter the same error, or rather do you have any idea on how to fix it?
Thanks in advance!
hello, I'm not sure I had that error , I can't totally remember but what I encountered might be a bug , the dmrtd lib was detecting a jpeg image as jp2 , so you might want to check before converting . Another issue was I had to convert the byte array to hex then check if the data is an image by check the FFD8 and FFD9 markers
Hello, thank you so much for your help, I managed to read it correctly by parsing the output with the following class (my offset was off by a little)!
This is the code for parsing if anybody else has this problem (It's a rewritten Dart Version of the similar DataGroup2 Swift File inside the NFCPassportReader Project)
class DataGroupTwo extends DataGroup {
int nrImages = 0;
int versionNumber = 0;
int lengthOfRecord = 0;
int numberOfFacialImages = 0;
int facialRecordDataLength = 0;
int nrFeaturePoints = 0;
int gender = 0;
int eyeColor = 0;
int hairColor = 0;
int featureMask = 0;
int expression = 0;
int poseAngle = 0;
int poseAngleUncertainty = 0;
int faceImageType = 0;
int imageDataType = 0;
int imageWidth = 0;
int imageHeight = 0;
int imageColorSpace = 0;
int sourceType = 0;
int deviceType = 0;
int quality = 0;
Uint8List imageData = Uint8List(0);
DataGroupTwo(List data) : super(data);
@OverRide
void parse(List data) {
var tag = getNextTag();
verifyTag(tag, 0x7F61);
getNextLength();
// Tag should be 0x02
tag = getNextTag();
verifyTag(tag, 0x02);
nrImages = getNextValue()[0];
// Next tag is 0x7F60
tag = getNextTag();
verifyTag(tag, 0x7F60);
getNextLength();
// Next tag is 0xA1 (Biometric Header Template) - don't care about this
tag = getNextTag();
verifyTag(tag, 0xA1);
getNextValue();
// Now we get to the good stuff - next tag is either 5F2E or 7F2E
tag = getNextTag();
verifyTagOneOf(tag, [0x5F2E, 0x7F2E]);
var value = getNextValue();
parseISO19794_5(value);
}
void parseISO19794_5(List data) {
var offset = 4;
versionNumber = binToInt(data.sublist(offset, offset + 4));
offset += 4;
lengthOfRecord = binToInt(data.sublist(offset, offset + 4));
offset += 4;
numberOfFacialImages = binToInt(data.sublist(offset, offset + 2));
offset += 2;
facialRecordDataLength = binToInt(data.sublist(offset, offset + 4));
offset += 4;
nrFeaturePoints = binToInt(data.sublist(offset, offset + 2));
offset += 2;
gender = binToInt(data.sublist(offset, offset + 1));
offset += 1;
eyeColor = binToInt(data.sublist(offset, offset + 1));
offset += 1;
hairColor = binToInt(data.sublist(offset, offset + 1));
offset += 1;
featureMask = binToInt(data.sublist(offset, offset + 3));
offset += 3;
expression = binToInt(data.sublist(offset, offset + 2));
offset += 2;
poseAngle = binToInt(data.sublist(offset, offset + 3));
offset += 3;
poseAngleUncertainty = binToInt(data.sublist(offset, offset + 3));
offset += 3;
// Skip over feature points if any are present
offset += nrFeaturePoints * 8;
faceImageType = binToInt(data.sublist(offset, offset + 1));
offset += 1;
imageDataType = binToInt(data.sublist(offset, offset + 1));
offset += 1;
imageWidth = binToInt(data.sublist(offset, offset + 2));
offset += 2;
imageHeight = binToInt(data.sublist(offset, offset + 2));
offset += 2;
imageColorSpace = binToInt(data.sublist(offset, offset + 1));
offset += 1;
sourceType = binToInt(data.sublist(offset, offset + 1));
offset += 1;
deviceType = binToInt(data.sublist(offset, offset + 2));
offset += 2;
quality = binToInt(data.sublist(offset, offset + 2));
offset += 2;
imageData = Uint8List.fromList(data.sublist(offset));
}
int binToInt(List bytes) {
return bytes.fold(0, (previous, byte) => (previous << 8) + byte);
}
}
Hello, thank you so much for your help, I managed to read it correctly by parsing the output with the following class (my offset was off by a little)!
This is the code for parsing if anybody else has this problem (It's a rewritten Dart Version of the similar DataGroup2 Swift File inside the NFCPassportReader Project)
class DataGroupTwo extends DataGroup { int nrImages = 0; int versionNumber = 0; int lengthOfRecord = 0; int numberOfFacialImages = 0; int facialRecordDataLength = 0; int nrFeaturePoints = 0; int gender = 0; int eyeColor = 0; int hairColor = 0; int featureMask = 0; int expression = 0; int poseAngle = 0; int poseAngleUncertainty = 0; int faceImageType = 0; int imageDataType = 0; int imageWidth = 0; int imageHeight = 0; int imageColorSpace = 0; int sourceType = 0; int deviceType = 0; int quality = 0; Uint8List imageData = Uint8List(0); DataGroupTwo(List data) : super(data); @OverRide void parse(List data) { var tag = getNextTag(); verifyTag(tag, 0x7F61); getNextLength(); // Tag should be 0x02 tag = getNextTag(); verifyTag(tag, 0x02); nrImages = getNextValue()[0]; // Next tag is 0x7F60 tag = getNextTag(); verifyTag(tag, 0x7F60); getNextLength(); // Next tag is 0xA1 (Biometric Header Template) - don't care about this tag = getNextTag(); verifyTag(tag, 0xA1); getNextValue(); // Now we get to the good stuff - next tag is either 5F2E or 7F2E tag = getNextTag(); verifyTagOneOf(tag, [0x5F2E, 0x7F2E]); var value = getNextValue(); parseISO19794_5(value); } void parseISO19794_5(List data) { var offset = 4; versionNumber = binToInt(data.sublist(offset, offset + 4)); offset += 4; lengthOfRecord = binToInt(data.sublist(offset, offset + 4)); offset += 4; numberOfFacialImages = binToInt(data.sublist(offset, offset + 2)); offset += 2; facialRecordDataLength = binToInt(data.sublist(offset, offset + 4)); offset += 4; nrFeaturePoints = binToInt(data.sublist(offset, offset + 2)); offset += 2; gender = binToInt(data.sublist(offset, offset + 1)); offset += 1; eyeColor = binToInt(data.sublist(offset, offset + 1)); offset += 1; hairColor = binToInt(data.sublist(offset, offset + 1)); offset += 1; featureMask = binToInt(data.sublist(offset, offset + 3)); offset += 3; expression = binToInt(data.sublist(offset, offset + 2)); offset += 2; poseAngle = binToInt(data.sublist(offset, offset + 3)); offset += 3; poseAngleUncertainty = binToInt(data.sublist(offset, offset + 3)); offset += 3; // Skip over feature points if any are present offset += nrFeaturePoints * 8; faceImageType = binToInt(data.sublist(offset, offset + 1)); offset += 1; imageDataType = binToInt(data.sublist(offset, offset + 1)); offset += 1; imageWidth = binToInt(data.sublist(offset, offset + 2)); offset += 2; imageHeight = binToInt(data.sublist(offset, offset + 2)); offset += 2; imageColorSpace = binToInt(data.sublist(offset, offset + 1)); offset += 1; sourceType = binToInt(data.sublist(offset, offset + 1)); offset += 1; deviceType = binToInt(data.sublist(offset, offset + 2)); offset += 2; quality = binToInt(data.sublist(offset, offset + 2)); offset += 2; imageData = Uint8List.fromList(data.sublist(offset)); } int binToInt(List bytes) { return bytes.fold(0, (previous, byte) => (previous << 8) + byte); } }
if you are working with swift I have someone looking an iOS implementation to convert the byte data from jp2
Hello, thank you so much for your help, I managed to read it correctly by parsing the output with the following class (my offset was off by a little)! This is the code for parsing if anybody else has this problem (It's a rewritten Dart Version of the similar DataGroup2 Swift File inside the NFCPassportReader Project)
class DataGroupTwo extends DataGroup { int nrImages = 0; int versionNumber = 0; int lengthOfRecord = 0; int numberOfFacialImages = 0; int facialRecordDataLength = 0; int nrFeaturePoints = 0; int gender = 0; int eyeColor = 0; int hairColor = 0; int featureMask = 0; int expression = 0; int poseAngle = 0; int poseAngleUncertainty = 0; int faceImageType = 0; int imageDataType = 0; int imageWidth = 0; int imageHeight = 0; int imageColorSpace = 0; int sourceType = 0; int deviceType = 0; int quality = 0; Uint8List imageData = Uint8List(0); DataGroupTwo(List data) : super(data); @OverRide void parse(List data) { var tag = getNextTag(); verifyTag(tag, 0x7F61); getNextLength(); // Tag should be 0x02 tag = getNextTag(); verifyTag(tag, 0x02); nrImages = getNextValue()[0]; // Next tag is 0x7F60 tag = getNextTag(); verifyTag(tag, 0x7F60); getNextLength(); // Next tag is 0xA1 (Biometric Header Template) - don't care about this tag = getNextTag(); verifyTag(tag, 0xA1); getNextValue(); // Now we get to the good stuff - next tag is either 5F2E or 7F2E tag = getNextTag(); verifyTagOneOf(tag, [0x5F2E, 0x7F2E]); var value = getNextValue(); parseISO19794_5(value); } void parseISO19794_5(List data) { var offset = 4; versionNumber = binToInt(data.sublist(offset, offset + 4)); offset += 4; lengthOfRecord = binToInt(data.sublist(offset, offset + 4)); offset += 4; numberOfFacialImages = binToInt(data.sublist(offset, offset + 2)); offset += 2; facialRecordDataLength = binToInt(data.sublist(offset, offset + 4)); offset += 4; nrFeaturePoints = binToInt(data.sublist(offset, offset + 2)); offset += 2; gender = binToInt(data.sublist(offset, offset + 1)); offset += 1; eyeColor = binToInt(data.sublist(offset, offset + 1)); offset += 1; hairColor = binToInt(data.sublist(offset, offset + 1)); offset += 1; featureMask = binToInt(data.sublist(offset, offset + 3)); offset += 3; expression = binToInt(data.sublist(offset, offset + 2)); offset += 2; poseAngle = binToInt(data.sublist(offset, offset + 3)); offset += 3; poseAngleUncertainty = binToInt(data.sublist(offset, offset + 3)); offset += 3; // Skip over feature points if any are present offset += nrFeaturePoints * 8; faceImageType = binToInt(data.sublist(offset, offset + 1)); offset += 1; imageDataType = binToInt(data.sublist(offset, offset + 1)); offset += 1; imageWidth = binToInt(data.sublist(offset, offset + 2)); offset += 2; imageHeight = binToInt(data.sublist(offset, offset + 2)); offset += 2; imageColorSpace = binToInt(data.sublist(offset, offset + 1)); offset += 1; sourceType = binToInt(data.sublist(offset, offset + 1)); offset += 1; deviceType = binToInt(data.sublist(offset, offset + 2)); offset += 2; quality = binToInt(data.sublist(offset, offset + 2)); offset += 2; imageData = Uint8List.fromList(data.sublist(offset)); } int binToInt(List bytes) { return bytes.fold(0, (previous, byte) => (previous << 8) + byte); } }
if you are working with swift I have someone looking an iOS implementation to convert the byte data from jp2
I got it working on iOS and android, using your solution on android and this method on iOS:
public func convertJpeg2000ToJpeg(jp2ImageData: Data) -> Data? {
// Initialize a CIImage with the JPEG2000 data
guard let ciImage = CIImage(data: jp2ImageData) else {
print("Failed to create CIImage")
return nil
}
// Create a context to convert the image
let context = CIContext(options: nil)
// Define JPEG compression quality
let jpegQuality: CGFloat = 0.9 // Adjust based on your quality requirements
// Get JPEG representation of the CIImage
guard let jpegData = context.jpegRepresentation(of: ciImage, colorSpace: ciImage.colorSpace ?? CGColorSpaceCreateDeviceRGB()) else {
print("Failed to get JPEG representation")
return nil
}
return jpegData
}
Hello, thank you so much for your help, I managed to read it correctly by parsing the output with the following class (my offset was off by a little)! This is the code for parsing if anybody else has this problem (It's a rewritten Dart Version of the similar DataGroup2 Swift File inside the NFCPassportReader Project)
class DataGroupTwo extends DataGroup { int nrImages = 0; int versionNumber = 0; int lengthOfRecord = 0; int numberOfFacialImages = 0; int facialRecordDataLength = 0; int nrFeaturePoints = 0; int gender = 0; int eyeColor = 0; int hairColor = 0; int featureMask = 0; int expression = 0; int poseAngle = 0; int poseAngleUncertainty = 0; int faceImageType = 0; int imageDataType = 0; int imageWidth = 0; int imageHeight = 0; int imageColorSpace = 0; int sourceType = 0; int deviceType = 0; int quality = 0; Uint8List imageData = Uint8List(0); DataGroupTwo(List data) : super(data); @OverRide void parse(List data) { var tag = getNextTag(); verifyTag(tag, 0x7F61); getNextLength(); // Tag should be 0x02 tag = getNextTag(); verifyTag(tag, 0x02); nrImages = getNextValue()[0]; // Next tag is 0x7F60 tag = getNextTag(); verifyTag(tag, 0x7F60); getNextLength(); // Next tag is 0xA1 (Biometric Header Template) - don't care about this tag = getNextTag(); verifyTag(tag, 0xA1); getNextValue(); // Now we get to the good stuff - next tag is either 5F2E or 7F2E tag = getNextTag(); verifyTagOneOf(tag, [0x5F2E, 0x7F2E]); var value = getNextValue(); parseISO19794_5(value); } void parseISO19794_5(List data) { var offset = 4; versionNumber = binToInt(data.sublist(offset, offset + 4)); offset += 4; lengthOfRecord = binToInt(data.sublist(offset, offset + 4)); offset += 4; numberOfFacialImages = binToInt(data.sublist(offset, offset + 2)); offset += 2; facialRecordDataLength = binToInt(data.sublist(offset, offset + 4)); offset += 4; nrFeaturePoints = binToInt(data.sublist(offset, offset + 2)); offset += 2; gender = binToInt(data.sublist(offset, offset + 1)); offset += 1; eyeColor = binToInt(data.sublist(offset, offset + 1)); offset += 1; hairColor = binToInt(data.sublist(offset, offset + 1)); offset += 1; featureMask = binToInt(data.sublist(offset, offset + 3)); offset += 3; expression = binToInt(data.sublist(offset, offset + 2)); offset += 2; poseAngle = binToInt(data.sublist(offset, offset + 3)); offset += 3; poseAngleUncertainty = binToInt(data.sublist(offset, offset + 3)); offset += 3; // Skip over feature points if any are present offset += nrFeaturePoints * 8; faceImageType = binToInt(data.sublist(offset, offset + 1)); offset += 1; imageDataType = binToInt(data.sublist(offset, offset + 1)); offset += 1; imageWidth = binToInt(data.sublist(offset, offset + 2)); offset += 2; imageHeight = binToInt(data.sublist(offset, offset + 2)); offset += 2; imageColorSpace = binToInt(data.sublist(offset, offset + 1)); offset += 1; sourceType = binToInt(data.sublist(offset, offset + 1)); offset += 1; deviceType = binToInt(data.sublist(offset, offset + 2)); offset += 2; quality = binToInt(data.sublist(offset, offset + 2)); offset += 2; imageData = Uint8List.fromList(data.sublist(offset)); } int binToInt(List bytes) { return bytes.fold(0, (previous, byte) => (previous << 8) + byte); } }
if you are working with swift I have someone looking an iOS implementation to convert the byte data from jp2
I got it working on iOS and android, using your solution on android and this method on iOS:
public func convertJpeg2000ToJpeg(jp2ImageData: Data) -> Data? { // Initialize a CIImage with the JPEG2000 data guard let ciImage = CIImage(data: jp2ImageData) else { print("Failed to create CIImage") return nil } // Create a context to convert the image let context = CIContext(options: nil) // Define JPEG compression quality let jpegQuality: CGFloat = 0.9 // Adjust based on your quality requirements // Get JPEG representation of the CIImage guard let jpegData = context.jpegRepresentation(of: ciImage, colorSpace: ciImage.colorSpace ?? CGColorSpaceCreateDeviceRGB()) else { print("Failed to get JPEG representation") return nil } return jpegData }
thanks alot
Hi,
I am trying to read image data from passport. How can i take photos from passport with using dmrtd ? I try to take bytearray from dg2 and transform to image with using Image.memory(bytes) but it doesnt work. İt give wrong data error. İs it another data in dg2 other than faceimages ?