这是一份用心整理的android面试总结英语,tensorflow-android 官方demo源码分析,成为阿里P7Android架构师到底有多难

previewWidth = size.getWidth();

// 相机捕获的图片的大小确定后,需要对捕获图片做裁剪等预操作。这将回调到ClassifierActivity中。我们后面重点分析。

CameraActivity.this.onPreviewSizeChosen(size, rotation);

}

}

我们这就分析清楚了打开摄像头前cameraConnectionCallback的回调流程了,还记得我们传入了另外一个listener吧,也就是onImageAvailableListener, 它在摄像头被打开后,捕获的图片available时由系统回调到。摄像头打开后,会create一个新的预览session,其中就会设置OnImageAvailableListener到CameraDevice中。这个过程我们不做详细分析了。

3.3、相机预览图片宽高确定后,回调onPreviewSizeChosen


上面分析到onPreviewSizeChosen会调用到ClassifierActivity中。它主要做了两件事,构造分类器classifier,它是模型分类预测的一个比较关键的类。另外就是预处理输入图片,如裁剪到和模型训练所使用的图片相同的尺寸。

// 图片预览展现出来时回调。主要是构造分类器classifier,和裁剪输入图片为224*224

@Override

public void onPreviewSizeChosen(final Size size, final int rotation) {

final float textSizePx = TypedValue.applyDimension(

TypedValue.COMPLEX_UNIT_DIP, TEXT_SIZE_DIP, getResources().getDisplayMetrics());

borderedText = new BorderedText(textSizePx);

borderedText.setTypeface(Typeface.MONOSPACE);

// 构造分类器,利用了TensorFlow训练出来的Model,也就是.pb文件。这是后面做物体分类识别的关键

classifier =

TensorFlowImageClassifier.create(

getAssets(),

MODEL_FILE,

LABEL_FILE,

INPUT_SIZE,

IMAGE_MEAN,

IMAGE_STD,

INPUT_NAME,

OUTPUT_NAME);

previewWidth = size.getWidth();

previewHeight = size.getHeight();

sensorOrientation = rotation - getScreenOrientation();

LOGGER.i(“Camera orientation relative to screen canvas: %d”, sensorOrientation);

LOGGER.i(“Initializing at size %dx%d”, previewWidth, previewHeight);

rgbFrameBitmap = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);

croppedBitmap = Bitmap.createBitmap(INPUT_SIZE, INPUT_SIZE, Config.ARGB_8888);

// 将照相机获取的原始图片,转换为224*224的图片,用来作为模型预测的输入。

frameToCropTransform = ImageUtils.getTransformationMatrix(

previewWidth, previewHeight,

INPUT_SIZE, INPUT_SIZE,

sensorOrientation, MAINTAIN_ASPECT);

cropToFrameTransform = new Matrix();

frameToCropTransform.invert(cropToFrameTransform);

addCallback(

new DrawCallback() {

@Override

public void drawCallback(final Canvas canvas) {

renderDebug(canvas);

}

});

}

3.3.1、分类器classifier的构造

classifier分类器是模型预测图片分类中比较重要的类,其中一些概念和深度学习以及TensorFlow紧密相关。代码如下

// 构造物体识别分类器

public static Classifier create(

AssetManager assetManager,

String modelFilename,

String labelFilename,

int inputSize,

int imageMean,

float imageStd,

String inputName,

String outputName) {

// 1 构造TensorFlowImageClassifier分类器,inputName和outputName分别为模型输入节点和输出节点的名字

TensorFlowImageClassifier c = new TensorFlowImageClassifier();

c.inputName = inputName;

c.outputName = outputName;

// 2 读取label文件内容,将内容设置到出classifier的labels数组中

String actualFilename = labelFilename.split(“file:///android_asset/”)[1];

Log.i(TAG, "Reading labels from: " + actualFilename);

BufferedReader br = null;

try {

// 读取label文件流,label文件表征了可以识别出来的物体分类。我们预测的物体名称就是其中之一。

br = new BufferedReader(new InputStreamReader(assetManager.open(actualFilename)));

// 将label存储到TensorFlowImageClassifier的labels数组中

String line;

while ((line = br.readLine()) != null) {

c.labels.add(line);

}

br.close();

} catch (IOException e) {

throw new RuntimeException(“Problem reading label file!” , e);

}

// 3 读取model文件名,并设置到classifier的interface变量中。

c.inferenceInterface = new TensorFlowInferenceInterface(assetManager, modelFilename);

// 4 利用输出节点名称,获取输出节点的shape,也就是最终分类的数目。

// 输出的shape为二维矩阵[N, NUM_CLASSES], N为batch size,也就是一批训练的图片个数。NUM_CLASSES为分类个数

final Operation operation = c.inferenceInterface.graphOperation(outputName);

final int numClasses = (int) operation.output(0).shape().size(1);

Log.i(TAG, "Read " + c.labels.size() + " labels, output layer size is " + numClasses);

// 5. 设置分类器的其他变量

c.inputSize = inputSize; // 物体分类预测时输入图片的尺寸。也就是相机原始图片裁剪后的图片。默认为224*224

c.imageMean = imageMean; // 像素点RGB通道的平均值,默认为117。用来将0~255的数值做归一化的

c.imageStd = imageStd; // 像素点RGB通道的归一化比例,默认为1

// 6. 分配Buffer给输出变量

c.outputNames = new String[] {outputName}; // 输出节点名字

c.intValues = new int[inputSize * inputSize];

c.floatValues = new float[inputSize * inputSize * 3]; // RGB三通道

c.outputs = new float[numClasses]; // 预测完的结果,也就是图片对应到每个分类的概率。我们取概率最大的前三个显示在app中

return c;

}

3.3.2、预处理预览图片

// 预处理预览图片,裁剪,旋转等操作。

// srcWidth, srcHeight为预览图片宽高。dstWidth dstHeight为训练模型时使用的图片的宽高

// applyRotation 旋转角度,必须是90的倍数,

// maintainAspectRatio 如果为true,旋转时缩放x而保证y不变

public static Matrix getTransformationMatrix(

final int srcWidth,

final int srcHeight,

final int dstWidth,

final int dstHeight,

final int applyRotation,

final boolean maintainAspectRatio) {

// 定义预处理后的图片像素矩阵

final Matrix matrix = new Matrix();

// 处理旋转

if (applyRotation != 0) {

// 旋转只能处理90度的倍数

if (applyRotation % 90 != 0) {

LOGGER.w(“Rotation of %d % 90 != 0”, applyRotation);

}

// translate平移,保持圆心不变

matrix.postTranslate(-srcWidth / 2.0f, -srcHeight / 2.0f);

// rotate旋转

matrix.postRotate(applyRotation);

}

// 输出矩阵是否需要转置。如果旋转为90度和270度时需要。转置后,宽高互换。

final boolean transpose = (Math.abs(applyRotation) + 90) % 180 == 0;

final int inWidth = transpose ? srcHeight : srcWidth;

final int inHeight = transpose ? srcWidth : srcHeight;

// 如果src尺寸和dest尺寸不同,则需要做裁剪

if (inWidth != dstWidth || inHeight != dstHeight) {

final float scaleFactorX = dstWidth / (float) inWidth;

final float scaleFactorY = dstHeight / (float) inHeight;

if (maintainAspectRatio) {

// 保持宽高比例不变,不会有形变,但可能会被剪切。此时宽高scale的因子相同

final float scaleFactor = Math.max(scaleFactorX, scaleFactorY);

matrix.postScale(scaleFactor, scaleFactor);

} else {

// 不用保持宽高不变,直接匹配为dest的尺寸。可能会发生形变

matrix.postScale(scaleFactorX, scaleFactorY);

}

}

if (applyRotation != 0) {

// 平移变换

matrix.postTranslate(dstWidth / 2.0f, dstHeight / 2.0f);

}

return matrix;

}

3.4、相机预览图片available时,OnImageAvailableListener回调


当相机预览图片准备好时,Android系统的cameraDevice会回调之前注册的OnImageAvailableListener。下面来看OnImageAvailableListener都做了哪些事情。

public void onImageAvailable(final ImageReader reader) {

// onPreviewSizeChosen被回调后,设置了previewWidth和previewHeight,才处理预览图片

if (previewWidth == 0 || previewHeight == 0) {

return;

}

// 构造图片输出矩阵

if (rgbBytes == null) {

rgbBytes = new int[previewWidth * previewHeight];

}

try {

// 获取图片

final Image image = reader.acquireLatestImage();

if (image == null) {

return;

}

// 正在处理图片时,则直接返回

if (isProcessingFrame) {

image.close();

return;

}

// yuv转换为rgb格式

isProcessingFrame = true;

Trace.beginSection(“imageAvailable”);

final Plane[] planes = image.getPlanes();

fillBytes(planes, yuvBytes);

yRowStride = planes[0].getRowStride();

final int uvRowStride = planes[1].getRowStride();

final int uvPixelStride = planes[1].getPixelStride();

imageConverter =

new Runnable() {

@Override

public void run() {

ImageUtils.convertYUV420ToARGB8888(

yuvBytes[0],

yuvBytes[1],

yuvBytes[2],

previewWidth,

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

总而言之,Android开发行业变化太快,作为技术人员就要保持终生学习的态度,让学习力成为核心竞争力,所谓“活到老学到老”只有不断的学习,不断的提升自己,才能跟紧行业的步伐,才能不被时代所淘汰。

在这里我分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

片转存中…(img-s6bJCIDg-1712545343127)]

还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 29
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值