您当前的位置: 首页 >  android

寒冰屋

暂无认证

  • 6浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

从Android上的相机裁剪图像

寒冰屋 发布时间:2019-02-20 20:24:18 ,浏览量:6

目录

介绍

使用代码

  • 下载源代码 - 1 MB
介绍

基本思路非常简单:

  • 显示相机预览
  • 添加矩形视图到相机预览(新裁剪图像的边界)
  • 裁剪图像并将结果保存到文件中
使用代码

让我们开始吧!在Android studio中创建一个新项目(我使用的是3.2.1版),或者您可以下载源文件并选择:File-New-Import项目。添加到build.gradle应用程序级别:

implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

我使用Butterknife库,它非常有用。此外,我们需要相机和写入权限,因此将其添加到AndroidManifest.xml。



我们需要2个片段,一个用于相机预览,另一个用于显示裁剪图像。创建新片段——ImageFragment(文件-新建-片段-片段(空白)——添加TextView和Imageview到layout-xml文件:




    

        

        

    

将一些代码添加到ImageFragment类中,它只显示image和textview信息:

public class ImageFragment extends Fragment {

    private Bitmap bitmap;

    @BindView(R.id.res_photo)
    ImageView resPhoto;

    @BindView(R.id.res_photo_size)
    TextView resPhotoSize;

    public void imageSetupFragment(Bitmap bitmap) {
        if (bitmap != null) {
            this.bitmap = bitmap;
        }
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setRetainInstance(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.fragment_image, container, false);
        ButterKnife.bind(this, view);
        //check if bitmap exist, set to ImageView
        if (bitmap != null) {
            resPhoto.setImageBitmap(bitmap);
            String info = "image with:" + bitmap.getWidth() + "\n" + 
                          "image height:" + bitmap.getHeight();
            resPhotoSize.setText(info);
        }
        return view;
    }
}

创建第二个片段—— PhotoFragment(文件-新建- Fragment- Fragment(空白)——向layout-xml文件添加一些组件。主要是SurfaceView(用于摄像机预览)和View(用于裁剪的边框)。




    

        

            

            
            
            

            
        
    

https://www.codeproject.com/KB/android/1276135/Screenshot_20190202-215707.jpg

我使用了一个自定义按钮(绿色按钮),这是简单的代码,但很棒的视图!为此,您需要在Res-drawable中创建一个新的XML文件,如photo_button.xml,根据需要设置形状,颜色或使用一些Web资源,如下所示:http://angrytools.com/android/button/。



    
    
    
    

然后将背景按钮设置为:

android:background="@drawable/photo_button"

此外,我们需要裁剪边框——它将是一个简单的矩形,为此,您需要在Res-drawable中创建一个新的XML文件,如border.xml:



    
    
    

并设置view-background为:

android:background="@drawable/border" />

下一步,PhotoFragment类。我们不能使用标准意图来制作照片,我们需要自定义函数,所以我们可以使用Camera类——它已被弃用,但仍然很好用,所以让我们使用它。该Camera类用来设置图像捕获设置,启动/停止预览,抓拍图片,并检索编码的视频帧。此类是Camera服务的客户端,它管理实际的相机硬件。

要控制预览,我们需要使用SurfaceHolder.Callback。这abstract interface是为了保持显示界面。允许您控制曲面大小和格式,编辑曲面中的像素以及监视曲面的更改。

public class PhotoFragment extends Fragment implements SurfaceHolder.Callback
{}

还实现一些方法:

@Override
public void surfaceCreated(SurfaceHolder holder) {

}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {

}

像这样:

@Override
public void surfaceCreated(SurfaceHolder holder) {
    camera = Camera.open();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    if (previewing) {
        camera.stopPreview();
        previewing = false;
    }

    if (camera != null) {
        try {
            Camera.Parameters parameters = camera.getParameters();
            //get preview sizes
            List previewSizes = parameters.getSupportedPreviewSizes();

            //find optimal - it very important
            previewSizeOptimal = getOptimalPreviewSize(previewSizes, parameters.getPictureSize().width,
                    parameters.getPictureSize().height);

            //set parameters
            if (previewSizeOptimal != null) {
                parameters.setPreviewSize(previewSizeOptimal.width, previewSizeOptimal.height);
            }

            if (camera.getParameters().getFocusMode().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
            }
            if (camera.getParameters().getFlashMode().contains(Camera.Parameters.FLASH_MODE_AUTO)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
            }

            camera.setParameters(parameters);

            //rotate screen, because camera sensor usually in landscape mode
            Display display = ((WindowManager) context.getSystemService
                                  (Context.WINDOW_SERVICE)).getDefaultDisplay();
            if (display.getRotation() == Surface.ROTATION_0) {
                camera.setDisplayOrientation(90);
            } else if (display.getRotation() == Surface.ROTATION_270) {
                camera.setDisplayOrientation(180);
            }

            //write some info
            int x1 = previewLayout.getWidth();
            int y1 = previewLayout.getHeight();

            int x2 = borderCamera.getWidth();
            int y2 = borderCamera.getHeight();

            String info = "Preview width:" + String.valueOf(x1) + "\n" + 
                                 "Preview height:" + String.valueOf(y1) + "\n" +
                    "Border width:" + String.valueOf(x2) + 
                                 "\n" + "Border height:" + String.valueOf(y2);
            resBorderSizeTV.setText(info);

            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
            previewing = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    camera.stopPreview();
    camera.release();
    camera = null;
    previewing = false;
}

设置正确的相机预览尺寸非常重要,如果图像的纵横比不适合预览相机尺寸——裁剪的图像尺寸不正确。  

public Camera.Size getOptimalPreviewSize(List sizes, int w, int h) {
    final double ASPECT_TOLERANCE = 0.1;
    double targetRatio = (double) w / h;
    if (sizes == null) return null;

    Camera.Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;

    int targetHeight = h;

    // Try to find an size match aspect ratio and size
    for (Camera.Size size : sizes) {
        double ratio = (double) size.width / size.height;
        if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
        if (Math.abs(size.height - targetHeight) < minDiff) {
            optimalSize = size;
            minDiff = Math.abs(size.height - targetHeight);
        }
    }

    // Cannot find the one match the aspect ratio, ignore the requirement
    if (optimalSize == null) {
        minDiff = Double.MAX_VALUE;
        for (Camera.Size size : sizes) {
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
    }
    return optimalSize;
}

定义一些功能来拍照:

@OnClick(R.id.make_photo_button)
void makePhoto() {
    if (camera != null) {
        camera.takePicture(myShutterCallback,
                myPictureCallback_RAW, myPictureCallback_JPG);
    }
}

和一些回调:

Camera.ShutterCallback myShutterCallback = new Camera.ShutterCallback() {
    @Override
    public void onShutter() {

    }
};

//leave it empty
Camera.PictureCallback myPictureCallback_RAW = new Camera.PictureCallback() {
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {

    }
};

//we need only JPG
Camera.PictureCallback myPictureCallback_JPG = new Camera.PictureCallback() {
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        Bitmap bitmapPicture
                = BitmapFactory.decodeByteArray(data, 0, data.length);

        Bitmap croppedBitmap = null;

        Display display = ((WindowManager) context.getSystemService
                              (Context.WINDOW_SERVICE)).getDefaultDisplay();
        if (display.getRotation() == Surface.ROTATION_0) {

            //rotate bitmap, because camera sensor usually in landscape mode
            Matrix matrix = new Matrix();
            matrix.postRotate(90);
            Bitmap rotatedBitmap = Bitmap.createBitmap(bitmapPicture, 0, 0, 
                       bitmapPicture.getWidth(), bitmapPicture.getHeight(), matrix, true);
            //save file
            createImageFile(rotatedBitmap);

            //calculate aspect ratio
            float koefX = (float) rotatedBitmap.getWidth() / (float) previewLayout.getWidth();
            float koefY = (float) rotatedBitmap.getHeight() / previewLayout.getHeight();

            //get viewfinder border size and position on the screen
            int x1 = borderCamera.getLeft();
            int y1 = borderCamera.getTop();

            int x2 = borderCamera.getWidth();
            int y2 = borderCamera.getHeight();

            //calculate position and size for cropping
            int cropStartX = Math.round(x1 * koefX);
            int cropStartY = Math.round(y1 * koefY);

            int cropWidthX = Math.round(x2 * koefX);
            int cropHeightY = Math.round(y2 * koefY);

            //check limits and make crop
            if (cropStartX + cropWidthX             
关注
打赏
1665926880
查看更多评论
0.2195s