Flutter中的Image类用于显示图像,他使用的压缩文件的图像格式,应该就是直接的jpg、png的文件吧,把它们加载到内存中的,然后用Uint8List的形式表示,交给Image控件就可以显示了。
这里Uint8List实际就是文件的字节数据罢了,关于这个字节数据有各种的表现形式 C++里面就是uint8 * uchar*,Java里面byte数组,byteBuffer,flutter里面就是这个uint8list了,还有byteBuffer,总结就是统统把它们当成字节数据就行了。
如果要对图像处理,压缩格式的图像很多情况下是不方便处理的,需要解码成位图形式,位图形式按照像素排列成矩阵,像素里面包含RGBA等通道,那么就方便处理很多了。 基本的流程就是 解码-处理-编码。 更全的流程就是 加载-----得到未解码的字节数组(可以显示等)----解码得到位图数组----按照位图处理----编码—显示或者保存
上面是原理,具体怎么写代码,使用什么样的API或者三方库呢?
如果是本地图片,直接读取图片文件的byte就行,readAsBytes。或者使用一些库,能够做缓存处理。 如果是网络图片,可以使用CachedNetworkImage,显示图片同时获取其字节数据就是图片文件,它支持缓存,https://github.com/Baseflow/flutter_cached_network_image/issues/714 还有方法,从网络获取图片文件不显示,也支持缓存的,然后获取字节数据,没找到目前
dart的image这个库可以用于图片解码image,将图片文件字节流数据传递给它的API解码就行,得到对应的Image对象,这个就是位图形式的图像了,通过它可以获取位图数组,里面就是像素数据,通道数据,图像的各种信息等,可以获取这些信息进行处理了
import 'package:image/image.dart' as img_lib;
/// 将压缩的图像数据转换为不压缩的像素组成的图像数据
/// 没压缩的是不能处理的,不能区argb通道,像素数量都是不对的,比如 100 * 100 的 四通道图像数据,压缩了可能是20000 字节
static img_lib.Image decodeImageBytes(Uint8List originalImageBytes) {
late final img_lib.Image? image;
final decoder = img_lib.findDecoderForData(originalImageBytes);
if (decoder == null) throw Exception('Image not supported');
image = decoder.decode(originalImageBytes);
if (image == null) throw Exception('Unable to decode image');
return image;
}
flutter中还提供了bytebuffer类,也可以通过相应的api和intlist进行转换 uintlist之间的转换,似乎可以通过fromList 进行转换 但是注意必须底层数据支持相互转换才行,否则就会数据错乱,3通道的图像用这个asUint32List转到c层的uint32list,结果坑大了,找了一天bug才找出来。asUint32List方法注释里面写了,结果没有仔细看。
参考文章 这个写得最好的找到的 库 https://github.com/khaifunglim97/flutter-image-processing
https://medium.com/@jeffrey.wolberg
显然的,flutter处理低效+很多图像处理库是C C++的所以要用它们处理。 首先学会flutter对C++的使用,利用ffi扩展库,参考官方教程比较简单的。
首先要学会C++的一些基本知识,然后需要会原生调用C++的一些基本知识,因为flutter需要利用原生的一些机制,这两个还是有一定的难度的。 主要是没有好的教程以及C C++体系本身的易用性差,问题多。
ffi提供了操作C++内存的接口 所以,在C++分配内存得到其指针 然后把数据复制过去,最后把指针传递给C++层就完成了图像数据的传递
// Credits to Tims !
extension Uint8ListBlobConversion on Uint8List {
Pointer allocatePointer() {
final blob = calloc(length);
final blobBytes = blob.asTypedList(length);
blobBytes.setAll(0, this);
return blob;
}
}
注意!踩坑点,flutter的int不能传给c++的int32,flutter是64位,传过去概率性变成莫名其妙的值,找bug找半天
另外方式:可以直接在C++读取图像,利用OpenCV使用OpenCV
https://github.com/westracer/flutter_native_opencv https://github.com/AdityaMulgundkar/flutter_opencv
使用ncnn https://github.com/KoheiKanagu/ncnn_yolox_flutter
使用tflite https://github.com/shaqian/flutter_tflite将C++图像返回给Flutter
参考
/// 调用高斯模糊,这里返回的是C++的usigned char * 指针{
final newBytes = blur(bytes, imgLengthBytes, 15);
if (newBytes == nullptr) {
print(‘高斯模糊失败’);
return null;
}var newList = newBytes.asTypedList(imgLengthBytes.value);
/// 释放指针
malloc.free(bytes);
malloc.free(imgLengthBytes);
return newList;}
压缩图像
flutter专门图片压缩库
前面提到的Image类提供了比较简单的压缩
C++直接用OpenCV压缩,然后传回地址也是可以的 https://medium.com/gitconnected/building-a-flutter-computer-vision-app-using-dart-ffi-opencv-and-tensorflow-part-2-81472b4ac380