如何创建尺寸为屏幕中心正方形的位图?

How to create a bitmap whose dimension is the central square of the screen?

背景与目标

我正在构建一个实时对象检测应用程序。为此,我需要一个 bitmap 来发送给识别算法。我有一个 activity 全屏显示相机输出,但我试图仅在屏幕的中央方块 上操作识别 。所以我需要一个具有这些尺寸和位置的位图,对应于 phone 屏幕的中央正方形。

我试过的

    @Override
    public void onPreviewSizeChosen(final Size size, final int rotation) {

        /* some code... */
    
        // Width of the screen (larger than the height), for my phone it's 4032
        previewWidth    = size.getWidth();
        // Height of the screen, for my phone it's 1980
        previewHeight   = size.getHeight();

        sensorOrientation = rotation - getScreenOrientation();

        // The bitmap which will be used to the recognition, dimension : full screen of the phone
        rgbFrameBitmap  = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);

        // squared bitmap 320x320, required size for the recognition algorithm
        croppedBitmap   = Bitmap.createBitmap(cropSize, cropSize, Config.ARGB_8888);

        // bitmap to croppedBitmap transform Matrix
        frameToCropTransform =
                ImageUtils.getTransformationMatrix(
                        previewWidth, previewHeight,
                        cropSize, cropSize,
                        sensorOrientation, MAINTAIN_ASPECT);

        cropToFrameTransform = new Matrix();
        frameToCropTransform.invert(cropToFrameTransform);

        trackingOverlay = (OverlayView) findViewById(R.id.tracking_overlay);
        trackingOverlay.addCallback(
                new DrawCallback() {
                    @Override
                    public void drawCallback(final Canvas canvas) {
                        tracker.draw(canvas, isDebug());
                    }
                });
        tracker.setFrameConfiguration(previewWidth, previewHeight, sensorOrientation);
       
    }

@Override
    protected void processImage() {

        trackingOverlay.postInvalidate();

        if (computingDetection) {
            readyForNextImage();
            return;
        }
        computingDetection = true;

        // THE LINE I CAN'T FIGURE OUT
        // I try to set width and height = previewHeight (1980 for my phone so a square)
        // and to move it down by (previewWidth-previewHeight)/2 so it goes in the middle of the screen
        rgbFrameBitmap.setPixels(getRgbBytes(), 0, previewWidth, 0, (previewWidth-previewHeight)/2, previewHeight, previewHeight);

        readyForNextImage();

        final Canvas canvas = new Canvas(croppedBitmap);
        canvas.drawBitmap(rgbFrameBitmap, frameToCropTransform, null);

        // Saved set to true, so I can check what the bitmap sent to the recognition algorithm looks like
        if (SAVE_PREVIEW_BITMAP) {
            ImageUtils.saveBitmap(croppedBitmap);
        }

        runInBackground(
                new Runnable() {
                    @Override
                    public void run() {
                        // Here we send the bitmap to the recognition algorithm to perform real-time detection
                        final List<Classifier.Recognition> results = detector.recognizeImage(croppedBitmap);


/* some code ... */

}

如果我将(previewWidth-previewHeight)/2替换为0作为rgbFrameBitmap.setPixels()函数的y参数,我设法在顶部的正方形中进行实时识别屏幕,如我所愿,除非它不居中。但是一旦我将 y 参数设置为 (previewWidth-previewHeight)/2,这就是我在尝试保存位图时遇到的错误:

W/ImageReader_JNI: Unable to acquire a buffer item, very likely client tried to acquire more than maxImages buffers

如何让位图在屏幕中间居中?

横屏

要获得屏幕的中央正方形区域,请比较屏幕的 heightwidth(以像素为单位)。通常对于桌面系统 width 更大,我假设是这种情况。

求出width-height的差,除以二,称其为x。 最后画一个从(x, 0)到(x+height, height)的矩形。

更一般

正方形的边长将为 length = min(width,height) 广场的坐标将是

x = (width-length)/2
y = (height-length)/2

最后画一个从(x, y)到(x+length, y+length)的矩形。

自行解决

上面的代码有两点我误解了。

首先,我混淆了 rgbFrameBitmap.setPixels() 函数的 xy 参数,但无论如何这只是在 space位图。要获得屏幕的中心,要改变的参数是offset,像这样:

int x = (previewWidth-previewHeight)/2;
rgbFrameBitmap.setPixels(getRgbBytes(), x, previewWidth, 0, 0, previewHeight, previewHeight);

此外,当我们将 croppedBitmap 发送到识别算法时,由于矩阵 frameToCropTransformrgbFrameBitmap 被裁剪为 320x320,我不得不更新它:

frameToCropTransform =
                ImageUtils.getTransformationMatrix(
                        previewWidth, previewHeight,
                        cropSize, cropSize,
                        sensorOrientation, MAINTAIN_ASPECT);

进入这个:

frameToCropTransform =
                ImageUtils.getTransformationMatrix(
                        previewHeight, previewHeight,
                        cropSize, cropSize,
                        sensorOrientation, MAINTAIN_ASPECT);

因为 rgbFrameBitmap 现在是一个尺寸为 previewHeightxpreviewHeight

的正方形