본문 바로가기

개발 이야기

Android | Camera 로 부터 들어온 영상편집

320x100

onPreviewFrame() 콜백 함수에서 왼쪽 및 오른쪽 테두리 부분(넓이 10픽셀, 높이는 영상 크기와 동일)을 자르는 코드는 아래와 같습니다. 코드를 작성하기 전에 YUV 데이터에 대한 이해가 중요합니다. 일반적인 카메라 데이터는 YUV420 color space 포맷으로 도착하며, 이 데이터를 처리하려면 해당 포맷에 대한 처리를 수행해야 합니다.

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class CustomPreviewCallback implements PreviewCallback {

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        Camera.Parameters parameters = camera.getParameters();
        Camera.Size size = parameters.getPreviewSize();
        int width = size.width;
        int height = size.height;

        // Create a new bitmap with the new width.
        int newWidth = width - 20; // Subtract 10 pixels from both left and right borders.
        Bitmap cropped = Bitmap.createBitmap(newWidth, height, Bitmap.Config.ARGB_8888);

        // Convert the YUV image to ARGB_8888
        int frameSize = width * height;
        int[] rgba = new int[frameSize];

        for (int i = 0, yIndex = 0; i < height; ++i) {
            for (int j = 0, xIndex = 0; j < width; ++j) {
                if (j >= 10 && j < width - 10) {
                    int y = (0xff & ((int) data[yIndex])) - 16;
                    if (y < 0) y = 0;
                    int indexCb = frameSize + (i >> 1) * width + (j & ~1);
                    int indexCr = frameSize + (i >> 1) * width + (j & ~1) + 1;
                    int cb = data[indexCb] - 128;
                    int cr = data[indexCr] - 128;
                    int r = (int) (1.164f * y + 1.596f * cr);
                    int g = (int) (1.164f * y - 0.813f * cr - 0.391f * cb);
                    int b = (int) (1.164f * y + 2.018f * cb);

                    r = clamp(r, 0, 255);
                    g = clamp(g, 0, 255);
                    b = clamp(b, 0, 255);

                    rgba[i * newWidth + xIndex++] = 0xff000000 | (r << 16) | (g << 8) | b;
                }
                yIndex++;
            }
        }

        cropped.setPixels(rgba, 0, newWidth, 0, 0, newWidth, height);

        // Process 'cropped' Bitmap as needed.
    }

    private int clamp(int value, int min, int max) {
        return Math.min(Math.max(value, min), max);
    }
}

이 코드는 미리보기에서 YUV420 데이터를 처리한 다음 왼쪽 및 오른쪽 각각 10픽셀을 제거한 비트맵을 생성합니다. 제거 후 생성된 비트맵은 cropped 변수에 저장되며 이를 필요에 따라 처리할 수 있습니다.

// Process 'cropped' Bitmap as needed.
부분에 대한 보충코드는 아래와 같다.

생성된 비트맵을 JPEG 형식으로 바꾸려면 ByteArrayOutputStream을 사용하여 비트맵을 바이트 배열로 변환한 후 원하는 형식으로 저장할 수 있습니다. 아래 코드에서는 생성된 비트맵을 JPEG로 변환하고 파일로 저장하는 방법을 보여주고 있습니다.

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.os.Environment;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class CustomPreviewCallback implements PreviewCallback {

    // ...

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        // ...

        // Process 'cropped' Bitmap as needed.
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        cropped.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        byte[] byteArray = stream.toByteArray();

        // Save the byteArray to a JPEG file.
        saveJpegFile(byteArray, "cropped_image.jpg");
    }

    private void saveJpegFile(byte[] byteArray, String fileName) {
        File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        File file = new File(storageDir, fileName);

        try (FileOutputStream fos = new FileOutputStream(file)) {
            fos.write(byteArray);
            fos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

위 코드는 cropped 비트맵을 JPEG 형식으로 변환하고 해당 이미지를 Environment.DIRECTORY_PICTURES 디렉터리에 "cropped_image.jpg"라는 파일로 저장합니다. 저장된 이미지에 액세스하려면 저장 경로를 변경하여 알맞게 설정하십시오.

참고로, Android 6.0 (API 레벨 23) 이상에서는 외부 저장소에 액세스하려면 동적 권한 요청이 필요합니다. 권한 요청과 관련한 자세한 정보는 안드로이드 개발자 문서를 참조하십시오.

반응형