I need to put a random image inside a screen with given resolution (640x480, 1280x720, etc). I finished it in Java. In Android do not support the BufferedImage and Graphics2D, I wonder if there is a way to replace this code from Java to Android. Here is my code from Java:
public BufferedImage resizeImage(BufferedImage originalImage, int type){
BufferedImage resizedImage = new BufferedImage(screenWidth, screenHeight, type);
Graphics2D g = resizedImage.createGraphics();
int imgWidth = originalImage.getWidth();
int imgHeight = originalImage.getHeight();
int newImgWidth = 0;
int newImgHeight = 0;
int X = 0;
int Y = 0;
if (imgWidth > screenWidth){
// scale width to fit
newImgWidth = screenWidth;
//scale height to maintain aspect ratio
newImgHeight = (newImgWidth * imgHeight) / imgWidth;
}
if (newImgHeight > screenHeight) {
//scale height to fit instead
newImgHeight = screenHeight;
//scale width to maintain aspect ratio
newImgWidth = (newImgHeight * imgWidth) / imgHeight;
}
if (imgWidth < screenWidth && imgHeight < screenHeight) {
X = screenWidth/2 - imgWidth/2;
Y = screenHeight/2 - imgHeight/2;
g.drawImage(originalImage, X, Y, imgWidth, imgHeight, null);
g.dispose();
return resizedImage;
}
X = screenWidth/2 - newImgWidth/2;
Y = screenHeight/2 - newImgHeight/2;
g.drawImage(originalImage, X, Y, newImgWidth, newImgHeight, null);
g.dispose();
return resizedImage;
}
Thank you in advance!
Related
I am looking to rotate an image that is loaded from files by 90 degrees. I have the code but when I use it, I am given an error saying that the coordinates are out of bounds. Any help would be appreciated.
Here is the method I have written so far:
public void rotateImage(OFImage image) {
if (currentImage != null) {
int width = image.getWidth();
int height = image.getHeight();
OFImage newImage = new OFImage(width, height);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
Color col = image.getPixel(i, j);
newImage.setPixel(height - j - 2, i, col);
}
}
image = newImage;
}
}
When you rotate the image by a certain angle, the resulting image becomes larger than the original one. The maximum image size is obtained when rotated by 45 degrees:
When creating a new image, you have to set its dimensions according to the rotated size:
public BufferedImage rotateImage(BufferedImage image, double angle) {
double radian = Math.toRadians(angle);
double sin = Math.abs(Math.sin(radian));
double cos = Math.abs(Math.cos(radian));
int width = image.getWidth();
int height = image.getHeight();
int nWidth = (int) Math.floor((double) width * cos + (double) height * sin);
int nHeight = (int) Math.floor((double) height * cos + (double) width * sin);
BufferedImage rotatedImage = new BufferedImage(
nWidth, nHeight, BufferedImage.TYPE_INT_ARGB);
// and so on...
return rotatedImage;
}
See also: Rotate a buffered image in Java
I was wondering if there is a way to resize a BufferedImage without creating a new instance of another image. I am wondering this because I think that it will be inefficient to create a new image each time I want to resize a BufferedImage for my application. Here is some code I've seen that explains what I don't want:
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
Image tmp = img.getScaledInstance(newW, newH, Image.SCALE_SMOOTH);
BufferedImage dimg = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = dimg.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return dimg;
}
public static BufferedImage scale(BufferedImage src, int w, int h)
{
BufferedImage img =
new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
int x, y;
int ww = src.getWidth();
int hh = src.getHeight();
int[] ys = new int[h];
for (y = 0; y < h; y++)
ys[y] = y * hh / h;
for (x = 0; x < w; x++) {
int newX = x * ww / w;
for (y = 0; y < h; y++) {
int col = src.getRGB(newX, ys[y]);
img.setRGB(x, y, col);
}
}
return img;
}
private BufferedImage resize(BufferedImage src, int targetSize) {
if (targetSize <= 0) {
return src; //this can't be resized
}
int targetWidth = targetSize;
int targetHeight = targetSize;
float ratio = ((float) src.getHeight() / (float) src.getWidth());
if (ratio <= 1) { //square or landscape-oriented image
targetHeight = (int) Math.ceil((float) targetWidth * ratio);
} else { //portrait image
targetWidth = Math.round((float) targetHeight / ratio);
}
BufferedImage bi = new BufferedImage(targetWidth, targetHeight, src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); //produces a balanced resizing (fast and decent quality)
g2d.drawImage(src, 0, 0, targetWidth, targetHeight, null);
g2d.dispose();
return bi;
}
Thank you for any responses!
I am trying to write a function that overlays an image at a rectangle with transparency over top of another image, However it doesn't layer the images it just erases the section that I overlay and the transparency cuts through the entire image. Here is my code.
public static void overlayImage(String imagePath, String overlayPath, int x, int y, int width, int height) {
Mat overlay = Imgcodecs.imread(overlayPath, Imgcodecs.IMREAD_UNCHANGED);
Mat image = Imgcodecs.imread(imagePath, Imgcodecs.IMREAD_UNCHANGED);
Rectangle rect = new Rectangle(x, y, width, height);
Imgproc.resize(overlay, overlay, rect.size());
Mat submat = image.submat(new Rect(rect.x, rect.y, overlay.cols(), overlay.rows()));
overlay.copyTo(submat);
Imgcodecs.imwrite(imagePath, image);
}
EDIT: Here are some example pictures:
Before:
After:
Found this function that does exactly what I needed.
public static void overlayImage(Mat background,Mat foreground,Mat output, Point location){
background.copyTo(output);
for(int y = (int) Math.max(location.y , 0); y < background.rows(); ++y){
int fY = (int) (y - location.y);
if(fY >= foreground.rows())
break;
for(int x = (int) Math.max(location.x, 0); x < background.cols(); ++x){
int fX = (int) (x - location.x);
if(fX >= foreground.cols()){
break;
}
double opacity;
double[] finalPixelValue = new double[4];
opacity = foreground.get(fY , fX)[3];
finalPixelValue[0] = background.get(y, x)[0];
finalPixelValue[1] = background.get(y, x)[1];
finalPixelValue[2] = background.get(y, x)[2];
finalPixelValue[3] = background.get(y, x)[3];
for(int c = 0; c < output.channels(); ++c){
if(opacity > 0){
double foregroundPx = foreground.get(fY, fX)[c];
double backgroundPx = background.get(y, x)[c];
float fOpacity = (float) (opacity / 255);
finalPixelValue[c] = ((backgroundPx * ( 1.0 - fOpacity)) + (foregroundPx * fOpacity));
if(c==3){
finalPixelValue[c] = foreground.get(fY,fX)[3];
}
}
}
output.put(y, x,finalPixelValue);
}
}
}
I'm trying to scale an image in relation to the width/height. Here is my method:
private byte[] scaleImage(Bitmap image) {
byte[] image = new byte[]{};
int width= image.getWidth();
int height = image.getHeight();
int wh = width / height ;
int hw = height / width ;
int newHeight, newWidth;
if (width> 250 || height> 250) {
if (width> height) { //landscape-mode
newHeight= 250;
newWidth = Math.round((int)(long)(250 * wh));
Bitmap sizeChanged = Bitmap.createScaledBitmap(image, newWidth, newHeight, true);
int bytes = størrelseEndret.getByteCount();
ByteBuffer bb = ByteBuffer.allocate(bytes);
sizeChanged.copyPixelsFromBuffer(bb);
image = bb.array();
} else { //portrait-mode
newWidth = 250;
newHeight = Math.round((int)(long)(250 * hw));
...same
}
}
return image;
}
After that, I wrote some codes to convert the image from Bitmapto byte[] array, but after a Debug I noticed that I'm getting really weird values. For example:
width = 640, height = 480, but wh = 1, hw = 0, newHeight = 200 and newWidth = 200?! I simply don't understand why? what am I doing wrong? Any help or hints is very appreciate. Thanks, Carl
You're running into a problem of integer arithmetic, basically - you're performing a division to get a scaling factor, but as an integer - so for something like 640x480, the scaling factors would be 1 and 0, because 640/480 is 1, and 480/640 is 0.
Instead of handling this as (x1/y1)*y2, you can change it to (x1*y2)/y1 so that you perform the division afterwards. So long as you don't overflow the integer limit in the multiplication (unlikely here) it should be fine. So I'd rewrite your code as:
private byte[] scaleImage(Bitmap image) {
byte[] image = new byte[]{};
int width = image.getWidth();
int height = image.getHeight();
int newHeight, newWidth;
if (width > 250 || height > 250) {
if (width > height) { //landscape-mode
newHeight = 250;
newWidth = (newHeight * width) / height;
} else {
newWidth = 250;
newHeight = (newWidth * height) / width;
}
} else {
// Whatever you want to do here
}
// Now use newWidth and newHeight
}
(I would definitely separate the "calculating newWidth and newHeight" from "performing the scaling" if possible, to avoid repeated code.)
The code below resizes a bitmap and keeps the aspect ratio.
I was wondering if there is a more efficient way of resizing, because i got the idea that i'm writing code that is already available in the android API.
private Bitmap resizeImage(Bitmap bitmap, int newSize){
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int newWidth = 0;
int newHeight = 0;
if(width > height){
newWidth = newSize;
newHeight = (newSize * height)/width;
} else if(width < height){
newHeight = newSize;
newWidth = (newSize * width)/height;
} else if (width == height){
newHeight = newSize;
newWidth = newSize;
}
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
width, height, matrix, true);
return resizedBitmap;
}
Use the method Bitmap.createScaledBitmap() :)
I really don't think so, I have done almost the same way as you, and got the idea from the android developer pages...
Depends on how you're using the image. If all you want to do is display the bitmap in a View, just call myImageView.setImageBitmap(bitmap) and let the framework resize it. But if you're concerned about memory, scaling first may be a good approach.