我有一个使用Camera API捕获图像的应用程序。该实现包含在一个CameraActivity类中,该类以我在此站点中看到的一些示例为模型。该应用程序目前正在数千部手机上正常运行;然而,今天,我收到了HTC Desire Z用户的一份报告,称我的图像被捕获为一系列垂直线条(不幸的是,我没有足够的点来发布图片,但如果有人愿意,我有足够的发送喜欢看一个)。我的应用程序中的图像预览和我保存的图像都是扭曲的。用户还确认他能够毫无问题地在我的应用程序之外使用他的手机相机。我已附上来源进行审核。
非常感谢任何帮助。
public class CameraActivity extends Activity {
public static final int FOTO_MODE = 0;
private SurfaceView preview = null;
private SurfaceHolder previewHolder = null;
private Camera camera = null;
private boolean inPreview = false;
private String receiptFileName = null;
SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(previewHolder);
} catch (Throwable t) {
Log.e("PreviewDemo-surfaceCallback", "Exception in setPreviewDisplay()", t);
Toast.makeText(CameraActivity.this, t.getMessage(), Toast.LENGTH_LONG).show();
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = getBestPreviewSize(width, height, parameters);
if (size != null) {
parameters.setPreviewSize(size.width, size.height);
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
camera.setParameters(parameters);
camera.startPreview();
inPreview = true;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// no-op
}
};
Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] imageData, Camera c) {
if (imageData != null) {
StoreByteImage(CameraActivity.this, imageData, 50, "ImageName");
camera.startPreview();
Intent resultIntent = new Intent();
resultIntent.putExtra(getString(R.string.receiptFileName), receiptFileName);
setResult(FOTO_MODE, resultIntent);
finish();
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.camera_preview);
receiptFileName = getIntent().getStringExtra(getString(R.string.receiptFileName));
ImageView shutterButton = (ImageView) findViewById(R.id.shutterButton);
shutterButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
takePicture();
}
});
shutterButton.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
focusOnly();
return false;
}
});
preview = (SurfaceView) findViewById(R.id.surface_camera);
previewHolder = preview.getHolder();
previewHolder.addCallback(surfaceCallback);
previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@Override
public void onResume() {
super.onResume();
camera = Camera.open();
}
@Override
public void onPause() {
if (inPreview) {
camera.stopPreview();
}
camera.release();
camera = null;
inPreview = false;
super.onPause();
}
private Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters) {
Camera.Size result = null;
for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
if (size.width <= width && size.height <= height) {
if (result == null) {
result = size;
} else {
int resultArea = result.width * result.height;
int newArea = size.width * size.height;
if (newArea > resultArea) {
result = size;
}
}
}
}
return result;
}
private void focusOnly() {
String focusMode = camera.getParameters().getFocusMode();
if (focusMode != null
&& (focusMode.equals(Camera.Parameters.FOCUS_MODE_AUTO) || focusMode
.equals(Camera.Parameters.FOCUS_MODE_MACRO))) {
Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
}
};
camera.autoFocus(autoFocusCallback);
}
}
private void takePicture() {
camera.takePicture(null, mPictureCallback, mPictureCallback);
}
private boolean StoreByteImage(Context mContext, byte[] imageData, int quality, String expName) {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
FileOutputStream fileOutputStream = null;
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bMap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
Matrix mat = new Matrix();
mat.postRotate(90);
Bitmap myImage = Bitmap.createBitmap(bMap, 0, 0, bMap.getWidth(), bMap.getHeight(), mat, true);
File path = new File(Environment.getExternalStorageDirectory() + ExpenseIt.SD_CARD_PATH_EXTENSION);
path.mkdirs();
fileOutputStream = new FileOutputStream(path + "/" + determineReceiptName());
BufferedOutputStream bos = new BufferedOutputStream(fileOutputStream);
myImage.compress(CompressFormat.JPEG, quality, bos);
bos.flush();
bos.close();
} catch (Exception e) {
FailureDialogFactory.getInstance().handleFailure(this, "Failure storing camera image.", e);
}
} else {
FailureDialogFactory.getInstance().handleFailure(this,
"Unable capture receipts while SD card is not available or phone is connected to computer.", null);
}
return true;
}
private String determineReceiptName() {
if (receiptFileName == null) {
receiptFileName = Calendar.getInstance().getTimeInMillis() + ".jpg";
}
return receiptFileName;
}
答案 0 :(得分:2)
我在自己的HTC Desire S相机项目中遇到了同样的问题。尽管getSupportedPictureSizes方法返回了大量支持的大小,但我发现我能够捕获的图片没有“垂直条”问题是宽高比最接近设备屏幕宽高比的问题。 例如:
当调用getSupportedPictureSizes方法时,我的HTC Desire S返回以下列表:
2592x1952,2592x1456,2592x1520,2592x1936,2592x1728,2592x1552,2048x1536,2048x1360,2048x1216,2048x1152,2048x1200,1600x1200,1584x1056,1280x960,1280x848,1280x768,1280x720,1280x752,1024x768,640x480,640x416,640x384,640x368,512x384,400x400,272x272
手机的屏幕分辨率为:
800x480
Aspect Ratio: 1.67
因此,如果我使用getSupportedPictureSizes方法返回的最大尺寸来捕获图像
2592x1952
Aspect Ratio: 1.32
结果:捕获的图像是一系列垂直条。 :(
所以我修改了CameraPreview example中给出的getOptimalPreviewSize方法,以返回最接近handet屏幕分辨率的分辨率列表。不是最有效的方法,但对我有用:
private List getOptimalPictureSizes(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
List<Size> finalSizes = new ArrayList();
// First filter out all sizes that have an aspect
// tolerance > 0.1
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) < ASPECT_TOLERANCE){
finalSizes.add(size);
}
}
// Remove sizes with same width but more deviation from the
// handset's aspect ratio (1.67 in my HTC Desire S)
List<Size> absoulteSizes = new ArrayList<Size>();
for(int i=0; i<finalSizes.size()-1; i++){
for(int j=i+1; j<finalSizes.size(); j++){
Size iSize = finalSizes.get(i);
Size jSize = finalSizes.get(j);
if(iSize.width != jSize.width)
continue;
double iRatio = (double)iSize.width/iSize.height;
double jRatio = (double)jSize.width/jSize.height;
if(Math.abs(targetRatio - iRatio) < Math.abs(targetRatio - jRatio)){
absoulteSizes.add(iSize);
}else if(Math.abs(targetRatio - iRatio) > Math.abs(targetRatio - jRatio)){
absoulteSizes.add(jSize);
}
else{
absoulteSizes.add(iSize);
absoulteSizes.add(jSize);
}
}
}
for(Size s:absoulteSizes){
Log.e("Preview", "COMPUTED::: "+s.width+"x"+s.height);
}
return absoulteSizes;
}
上述方法为我提供了以下过滤列表:
2592x1552 Aspect Ratio: 1.67
2048x1216 Aspect Ratio: 1.684
1280x768 Aspect Ratio: 1.67
640x384 Aspect Ratio: 1.67
所有4种尺寸都适用于我的相机而没有垂直条问题。同样值得注意的是,手机的相机应用程序中也建议使用相同的4种尺寸。
希望这有帮助。