#android-camera
#android-камера
Вопрос:
У меня есть приложение, которое использует 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 amp;amp; 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 resu<
}
private void focusOnly() {
String focusMode = camera.getParameters().getFocusMode();
if (focusMode != null
amp;amp; (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;
}
Ответ №1:
Я боролся с той же проблемой в моем собственном проекте камеры на моем HTC Desire S. Несмотря на то, что метод getSupportedPictureSizes вернул огромный список поддерживаемых размеров, я обнаружил, что снимки, которые я смог сделать без проблемы с «вертикальной полосой», были теми, соотношение сторон которых наиболее точно соответствовало соотношению сторон экрана устройства. Пример:
Мой HTC Desire S возвращает следующий список при вызове метода getSupportedPictureSizes:
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
Результат: полученное изображение представляет собой серию вертикальных полос.
Поэтому я изменил метод getOptimalPreviewSize, как указано здесь в примере CameraPreview, чтобы вернуть список разрешений, которые наиболее близки к разрешению экрана 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 размера предлагаются и в приложении для стандартной камеры телефона.
Надеюсь, это поможет.
Комментарии:
1. Спасибо за идею, но это не сработало. Я включил вашу идею в свой метод getBestPreviewSize (), но в результате все равно получились вертикальные линии.
2. @user657719 Просто для пояснения, вышеуказанные четыре размера разрешения должны вводиться в метод * setPictureSize(), а не в метод setPreviewSize(). Вы нашли другой способ обойти проблему?
3. Приятель, если установить допуск на 0,1, это приведет к сбоям в работе таких устройств, как Galaxy Ace, все поддерживаемые размеры изображений которых имеют соотношение 1,3.