javaandroidimage-processingandroid-bitmapgenerative-adversarial-network

Blending bitmap tiles


I'm using a GAN tflite model to process a bitmap. T model only accepts images of 512 x 512 pixels, so I first crop the image into tiles and then process all tiles at once. This resulted in an image full of seams and lines, so I added a code to blend the edges together.

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = findViewById(R.id.imageView); picture = findViewById(R.id.button); MIRNetConverter.initialize(MainActivity.this); uploadButton = findViewById(R.id.upload);

    uploadButton.setOnClickListener(new View.OnClickListener() {
        @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
        @Override
        public void onClick(View view) {
            if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_MEDIA_IMAGES}, 1);
            } else {
                uploadImage();
            }
        }
    });

  
    processingDialog = new Dialog(this);
    processingDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    processingDialog.setCancelable(false);
    processingDialog.setContentView(R.layout.processing_screen);
    executorService = Executors.newFixedThreadPool(4);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (executorService != null) {
        executorService.shutdown();
    }
}

private void showProcessingDialog() {
    if (processingDialog != null && !processingDialog.isShowing()) {
        ProgressBar progressBar = processingDialog.findViewById(R.id.progressBar);
        TextView textView = processingDialog.findViewById(R.id.textView);
        if (progressBar != null && textView != null) {
            progressBar.setIndeterminate(true);
            textView.setText("Processing...");
        }
        processingDialog.show();
    }
}

private void uploadImage() {
    Intent galleryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(galleryIntent, 2);
}

private void hideProcessingDialog() {
    if (processingDialog != null && processingDialog.isShowing()) {
        processingDialog.dismiss();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
        if (requestCode == 1 && data != null) {
            Bitmap image = (Bitmap) data.getExtras().get("data");
            imageView.setImageBitmap(image);
            showProcessingDialog();
            processImage(image);
        } else if (requestCode == 2 && data != null) {
            try {
                Bitmap image = MediaStore.Images.Media.getBitmap(this.getContentResolver(), data.getData());
                imageView.setImageBitmap(image);
                showProcessingDialog();
                processImage(image);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

private void processImage(Bitmap image) {
    executorService.execute(new Runnable() {
        @Override
        public void run() {
            int width = image.getWidth();
            int height = image.getHeight();
            Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            int numTilesX = (int) Math.ceil((float) width / (TILE_SIZE - OVERLAP));
            int numTilesY = (int) Math.ceil((float) height / (TILE_SIZE - OVERLAP));

            for (int y = 0; y < numTilesY; y++) {
                for (int x = 0; x < numTilesX; x++) {
                    int startX = x * (TILE_SIZE - OVERLAP);
                    int startY = y * (TILE_SIZE - OVERLAP);
                    int endX = Math.min(startX + TILE_SIZE, width);
                    int endY = Math.min(startY + TILE_SIZE, height);

                    startX = Math.max(startX, 0);
                    startY = Math.max(startY, 0);

                    Bitmap tileBitmap = Bitmap.createBitmap(image, startX, startY, endX - startX, endY - startY);
                    Bitmap processedTile = processTile(tileBitmap);

                    int drawX = x * (TILE_SIZE - OVERLAP);
                    int drawY = y * (TILE_SIZE - OVERLAP);

                    blendTiles(resultBitmap, processedTile, drawX, drawY, startX, startY, endX, endY);
                }
            }

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    imageView.setImageBitmap(resultBitmap);
                    hideProcessingDialog();
                }
            });
        }
    });
}

private void blendTiles(Bitmap result, Bitmap tile, int drawX, int drawY, int startX, int startY, int endX, int endY) {
    Canvas canvas = new Canvas(result);
    Paint paint = new Paint();
    paint.setAntiAlias(true);

    int tileWidth = tile.getWidth();
    int tileHeight = tile.getHeight();

    for (int i = 0; i < tileWidth; i++) {
        for (int j = 0; j < tileHeight; j++) {
            int tilePixel = tile.getPixel(i, j);
            int resultPixelX = drawX + i;
            int resultPixelY = drawY + j;

            if (resultPixelX < endX && resultPixelY < endY) {
                int resultPixel = result.getPixel(resultPixelX, resultPixelY);

                float alpha = calculateAlpha(i, j, tileWidth, tileHeight);
                int blendedPixel = blendPixels(resultPixel, tilePixel, alpha);

                result.setPixel(resultPixelX, resultPixelY, blendedPixel);
            }
        }
    }
}

private float calculateAlpha(int x, int y, int width, int height) {
    float edgeX = (x < OVERLAP) ? (x / (float) OVERLAP) : ((width - x - 1) < OVERLAP) ? ((width - x - 1) / (float) OVERLAP) : 1.0f;
    float edgeY = (y < OVERLAP) ? (y / (float) OVERLAP) : ((height - y - 1) < OVERLAP) ? ((height - y - 1) / (float) OVERLAP) : 1.0f;
    return Math.min(edgeX, edgeY);
}

private int blendPixels(int basePixel, int topPixel, float alpha) {
    int baseAlpha = Color.alpha(basePixel);
    int baseRed = Color.red(basePixel);
    int baseGreen = Color.green(basePixel);
    int baseBlue = Color.blue(basePixel);

    int topAlpha = Color.alpha(topPixel);
    int topRed = Color.red(topPixel);
    int topGreen = Color.green(topPixel);
    int topBlue = Color.blue(topPixel);

    int blendedAlpha = (int) (baseAlpha * (1 - alpha) + topAlpha * alpha);
    int blendedRed = (int) (baseRed * (1 - alpha) + topRed * alpha);
    int blendedGreen = (int) (baseGreen * (1 - alpha) + topGreen * alpha);
    int blendedBlue = (int) (baseBlue * (1 - alpha) + topBlue * alpha);

    return Color.argb(blendedAlpha, blendedRed, blendedGreen, blendedBlue);
}

private Bitmap processTile(Bitmap tile) {
    Bitmap resizedTile = Bitmap.createScaledBitmap(tile, TILE_SIZE, TILE_SIZE, true);
    Bitmap normalTile = MIRNetConverter.convertToNormal(MainActivity.this, resizedTile);
    return Bitmap.createScaledBitmap(normalTile, tile.getWidth(), tile.getHeight(), true);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == 100) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            startCamera();
        }
    } else if (requestCode == 1) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            uploadImage();
        } else {
            Toast.makeText(this, "Permission denied to read images from media storage", Toast.LENGTH_SHORT).show();
        }
    }
}

My code kind of works, but the edges are now extremely dark. Any opinion on why it's the case?

example result


Solution

  • The problem was that during blending, sometimes a tile doesn't have a tile next to itself, so you have to first check that topPixel and basePixel aren't black in order to blend them correctly.

     private boolean isBlackPixel(int pixel) {
            return Color.red(pixel) == 0 && Color.green(pixel) == 0 && Color.blue(pixel) == 0;
        }
        private void blendTiles(Bitmap result, Bitmap tile, int drawX, int drawY, int startX, int startY, int endX, int endY) {
            Canvas canvas = new Canvas(result);
            Paint paint = new Paint();
            paint.setAntiAlias(true);
    
            int tileWidth = tile.getWidth();
            int tileHeight = tile.getHeight();
    
            for (int i = 0; i < tileWidth; i++) {
                for (int j = 0; j < tileHeight; j++) {
                    int tilePixel = tile.getPixel(i, j);
                    int resultPixelX = drawX + i;
                    int resultPixelY = drawY + j;
    
                    if (resultPixelX < endX && resultPixelY < endY) {
                        int resultPixel = result.getPixel(resultPixelX, resultPixelY);
    
                        if (!isBlackPixel(tilePixel) && !isBlackPixel(resultPixel)) {
                            float alpha = calculateAlpha(i, j, tileWidth, tileHeight);
                            int blendedPixel = blendPixels(resultPixel, tilePixel, alpha);
    
                            result.setPixel(resultPixelX, resultPixelY, blendedPixel);
                        } else {
                            result.setPixel(resultPixelX, resultPixelY, isBlackPixel(resultPixel) ? tilePixel : resultPixel);
                        }
                    }
                }
            }
        }
    
        private float calculateAlpha(int x, int y, int width, int height) {
            float edgeX = (x < OVERLAP) ? (x / (float) OVERLAP) : ((width - x - 1) < OVERLAP) ? ((width - x - 1) / (float) OVERLAP) : 1.0f;
            float edgeY = (y < OVERLAP) ? (y / (float) OVERLAP) : ((height - y - 1) < OVERLAP) ? ((height - y - 1) / (float) OVERLAP) : 1.0f;
            return Math.min(edgeX, edgeY);
        }
    
        private int blendPixels(int basePixel, int topPixel, float alpha) {
            int baseAlpha = Color.alpha(basePixel);
            int baseRed = Color.red(basePixel);
            int baseGreen = Color.green(basePixel);
            int baseBlue = Color.blue(basePixel);
    
            int topAlpha = Color.alpha(topPixel);
            int topRed = Color.red(topPixel);
            int topGreen = Color.green(topPixel);
            int topBlue = Color.blue(topPixel);
    
            int blendedAlpha = (int) (baseAlpha * (1 - alpha) + topAlpha * alpha);
            int blendedRed = (int) (baseRed * (1 - alpha) + topRed * alpha);
            int blendedGreen = (int) (baseGreen * (1 - alpha) + topGreen * alpha);
            int blendedBlue = (int) (baseBlue * (1 - alpha) + topBlue * alpha);
    
            return Color.argb(blendedAlpha, blendedRed, blendedGreen, blendedBlue);
        }