Image cropping reduces quality, and the border looks bad

Using some math, I created the following java function to introduce a Bitmap, and cut out a square with the center at which the circle is cropped again with a black border around it. The rest of the area should be transparent. In addition, there is a transparent distance to the sides so as not to damage the preview when sending images through Messengers.

My function code is as follows:

public static Bitmap edit_image(Bitmap src,boolean makeborder) { int width = src.getWidth(); int height = src.getHeight(); int A, R, G, B; int pixel; int middlex = width/2; int middley = height/2; int seitenlaenge,startx,starty; if(width>height) { seitenlaenge=height; starty=0; startx = middlex - (seitenlaenge/2); } else { seitenlaenge=width; startx=0; starty = middley - (seitenlaenge/2); } int kreisradius = seitenlaenge/2; int mittx = startx + kreisradius; int mitty = starty + kreisradius; int border=2; int seitenabstand=55; Bitmap bmOut = Bitmap.createBitmap(seitenlaenge+seitenabstand, seitenlaenge+seitenabstand, Bitmap.Config.ARGB_8888); bmOut.setHasAlpha(true); for(int x = 0; x < width; ++x) { for(int y = 0; y < height; ++y) { int distzumitte = (int) (Math.pow(mittx-x,2) + Math.pow(mitty-y,2)); // (Xm-Xp)^2 + (Ym-Yp)^2 = dist^2 distzumitte = (int) Math.sqrt(distzumitte); pixel = src.getPixel(x, y); A = Color.alpha(pixel); R = (int)Color.red(pixel); G = (int)Color.green(pixel); B = (int)Color.blue(pixel); int color = Color.argb(A, R, G, B); int afterx=x-startx+(seitenabstand/2); int aftery=y-starty+(seitenabstand/2); if(x < startx || y < starty || afterx>=seitenlaenge+seitenabstand || aftery>=seitenlaenge+seitenabstand) //seitenrand { continue; } else if(distzumitte > kreisradius) { color=0x00FFFFFF; } else if(distzumitte > kreisradius-border && makeborder) //border { color = Color.argb(A, 0, 0, 0); } bmOut.setPixel(afterx, aftery, color); } } return bmOut; } 

This function works fine, but there are some problems associated with the fact that I still could not solve.

  • Image quality is greatly reduced.
  • The border is not very round, but it seems flat along the edges of the image (on some devices !!)

I would be grateful for any help in solving these problems. I must admit that I am not the best in mathematics, and probably there should be a better formula for limiting the boundary.

+6
source share
3 answers

your source code is hard to read, as it is a combination of German and English in variable names. Also, you donโ€™t say which image library you are using, so we donโ€™t know exactly where the Bitmap and Color classes came from.

In any case, it is very obvious that you only work with a bitmap image. A bitmap means that the entire image is stored in pixels of RAM per pixel. No lossy compression. I donโ€™t see anything in your source code that can affect image quality.

It is very likely that the answer is indicated in the Code, which you do not show us. In addition, what you describe (botrh problems) sounds like very typical low quality JPEG compression. I'm sure somewhere after calling the function you convert / save the image in JPEG format. Try to do this in this position in BMP, TIFF or PNG and make sure that the error has magically disappeared. Perhaps you can also set the JPEG quality level somewhere to avoid this.

To make it easier for others to (possibly) also find a good answer, let me translate your code into English:

  public static Bitmap edit_image(Bitmap src,boolean makeborder) { int width = src.getWidth(); int height = src.getHeight(); int A, R, G, B; int pixel; int middlex = width/2; int middley = height/2; int sideLength,startx,starty; if(width>height) { sideLength=height; starty=0; startx = middlex - (sideLength/2); } else { sideLength=width; startx=0; starty = middley - (sideLength/2); } int circleRadius = sideLength/2; int middleX = startx + circleRadius; int middleY = starty + circleRadius; int border=2; int sideDistance=55; Bitmap bmOut = Bitmap.createBitmap(sideLength+sideDistance, sideLength+sideDistance, Bitmap.Config.ARGB_8888); bmOut.setHasAlpha(true); for(int x = 0; x < width; ++x) { for(int y = 0; y < height; ++y) { int distanceToMiddle = (int) (Math.pow(middleX-x,2) + Math.pow(middleY-y,2)); // (Xm-Xp)^2 + (Ym-Yp)^2 = dist^2 distanceToMiddle = (int) Math.sqrt(distanceToMiddle); pixel = src.getPixel(x, y); A = Color.alpha(pixel); R = (int)Color.red(pixel); G = (int)Color.green(pixel); B = (int)Color.blue(pixel); int color = Color.argb(A, R, G, B); int afterx=x-startx+(sideDistance/2); int aftery=y-starty+(sideDistance/2); if(x < startx || y < starty || afterx>=sideLength+sideDistance || aftery>=sideLength+sideDistance) //margin { continue; } else if(distanceToMiddle > circleRadius) { color=0x00FFFFFF; } else if(distanceToMiddle > circleRadius-border && makeborder) //border { color = Color.argb(A, 0, 0, 0); } bmOut.setPixel(afterx, aftery, color); } } return bmOut; } 
+3
source

I think you need to check out PorterDuffXferMode.

You will find technical information about arranging image modes HERE .

There is a good example of creating a bitmap with rounded edges HERE . You just need to tweak the source code a bit and you're ready to go ...

Hope this helps.

+1
source

As for quality, I see nothing wrong with your method. Running code using Java Swing is not lost. The only problem is that the image has aliases.

The anti-aliasing problem will disappear as the screen resolution increases and will be more noticeable for lower resolutions. This may explain why you only see it on some devices. The same problem applies to your border, but in this case it will be more noticeable as the color is black.

Your algorithm determines the square area of โ€‹โ€‹the original image. To find a square, it starts at the center of the image and expands to the width or height image, whichever is smaller. I mean this area as square .

The aliasing is triggered by your code that sets the colors (I use pseudocode):

 if ( outOfSquare() ) { continue; // case 1: this works but you depend upon the new image' s default pixel value ie transparent black } else if ( insideSquare() && ! insideCircle() ) { color = 0x00FFFFFF; // case 2: transparent white. <- Redundant } else if ( insideBorder() ) { color = Color.argb(A, 0, 0, 0); // case 3: Black color using the transparency of the original image. } else { // inside the inner circle // case 4: leave image color } 

Some notes about the code:

  • Case 1 depends on the default pixel value of the original image, i.e. transparent black. It works, but itโ€™s better to install it explicitly
  • Case 2 is redundant. Handle it the same way you handle register 1. We are only interested in what happens inside the circle.
  • Case 3 (when you draw the border) is unclear what he expects. Using the alpha of the original image can ruin your new image if it happens that the original alpha changes around the edges of the circle. So this is clearly wrong and image dependent, could potentially be another cause of your problems.
  • Case 4 is fine.

Now on the periphery of your circle the following color transitions occur:

  • If the border is not used: full transparency -> color of the full image (case 2 and 4 in pseudo-code)
  • If the border is used: full transparency -> full black -> color of the full image (cases 2, 3 and 4)

To achieve better quality around the edges, you need to enter some intermediate states that will make the transitions smoother (new transitions are shown in italics):

  • The border is not used: full transparency -> partial transparency with the image color -> full image color
  • The border is used: full transparency -> partial transparency of black color -> full black color -> partial transparency of black color + image color (i.e. blending) -> Image color

I hope this helps

+1
source

Source: https://habr.com/ru/post/949657/


All Articles