4. JPEG质量参数调整
任务说明
本案例在Android Camera2相机预览回调中获取YUV_420_888图像,使用不同质量参数(80/90/100)执行JPEG编码,对比输出文件大小与图像清晰度差异。通过实际编码结果验证质量参数对压缩率与画质的影响规律。
核心实现
// JpegQualityEncoder.java
public class JpegQualityEncoder {
private static final String TAG = "JpegQualityEncoder";
private ImageReader mImageReader;
private File mOutputDir;
public JpegQualityEncoder(Context context) {
mOutputDir = new File(context.getExternalFilesDir(null), "jpeg_quality_test");
if (!mOutputDir.exists()) mOutputDir.mkdirs();
}
// 创建不同质量参数的ImageReader(质量通过编码阶段控制)
public ImageReader createImageReader(int width, int height) {
mImageReader = ImageReader.newInstance(width, height, ImageFormat.YUV_420_888, 2);
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image != null) {
// 分别用三种质量编码同一帧
encodeWithQualities(image, new int[]{80, 90, 100});
image.close();
}
}
}, null);
return mImageReader;
}
private void encodeWithQualities(Image image, int[] qualities) {
ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();
int ySize = yBuffer.remaining();
int uSize = uBuffer.remaining();
int vSize = vBuffer.remaining();
byte[] nv21 = new byte[ySize + uSize + vSize];
yBuffer.get(nv21, 0, ySize);
vBuffer.get(nv21, ySize, vSize);
uBuffer.get(nv21, ySize + vSize, uSize);
for (int quality : qualities) {
encodeJpeg(nv21, image.getWidth(), image.getHeight(), quality);
}
}
private void encodeJpeg(byte[] nv21, int width, int height, int quality) {
YuvImage yuvImage = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
yuvImage.compressToJpeg(new Rect(0, 0, width, height), quality, outStream);
File outputFile = new File(mOutputDir, "quality_" + quality + "_" + System.currentTimeMillis() + ".jpg");
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
fos.write(outStream.toByteArray());
Log.d(TAG, "Quality=" + quality + ", Size=" + outStream.size() + " bytes, File=" + outputFile.getName());
} catch (IOException e) {
Log.e(TAG, "Save failed: " + e.getMessage());
}
}
}
集成到Camera2预览
// 在Camera2配置中替换原有ImageReader
JpegQualityEncoder encoder = new JpegQualityEncoder(this);
mImageReader = encoder.createImageReader(640, 480);
// 创建CaptureSession时使用该ImageReader的Surface
List<Surface> surfaces = new ArrayList<>();
Surface imageSurface = mImageReader.getSurface();
surfaces.add(imageSurface);
// 如有预览需求可同时添加mPreviewSurface
验证方法
- 运行应用并授权相机权限,应用启动后自动捕获一帧图像并分别以质量80/90/100编码保存
- 打开Logcat过滤“JpegQualityEncoder”,观察三条日志记录,确认三个文件均已生成
- 使用文件管理器进入
Android/data/<包名>/files/jpeg_quality_test/目录,查看三个JPEG文件 - 对比文件大小:预期质量100文件最大,质量80文件最小(具体数值取决于图像内容,但大小顺序固定)
- 将三个文件传输到电脑或支持图片缩放的设备,放大同一细节区域对比清晰度:质量100边缘最锐利,质量80可能出现块状模糊
判定标准:三个质量参数均成功生成JPEG文件;文件大小随质量值递减;视觉上质量100图像细节保留最完整,质量80图像存在可感知的压缩伪影。