Related
The task is to crop video by given points(like rectangle) and display the cropped video.
The code works with cropping the first halh of video(0, 0, videoWidth/2, videoHeight). But when i tryed to display the second one(videoWidth/2, 0, videoWidth, videoHeight), that is what was displayed.
The video is displayed on TextureView inside FrameLayout.
The part, that doesn't work:
private void updateTextureViewSize(int ax, int ay, int bx, int by) {
float scaleX;
float scaleY;
//proportions between screen and frame dimensions
scaleX = mVideoWidth / mDisplayWidth;
scaleY = mVideoHeight / mDisplayHeight;
float scaleRegionW = mVideoWidth / Math.abs(ax - bx);
float scaleRegionH = mVideoHeight / Math.abs(ay - by);
float scaleRegion = scaleRegionW < scaleRegionH ? scaleRegionW : scaleRegionH;
Matrix matrix = new Matrix();
if (scaleX > scaleY) {
matrix.setScale(scaleRegion / scaleY, scaleRegion);
matrix.postTranslate(-ax * (int) scaleRegion / scaleY, -ay * scaleRegion / scaleY);
} else {
matrix.setScale(scaleRegion, scaleRegion / scaleX);
matrix.postTranslate(-ax * scaleRegion / scaleX, -ay * scaleRegion / scaleX);
}
mTextureView.setTransform(matrix);
mTextureView.setLayoutParams(new FrameLayout.LayoutParams((int) mDisplayWidth, (int) mDisplayHeight));
}
The answer was found by Dmitry Yacenko.
The full code.
An easy way to crop video by given points (ax, ay, bx, xy) is:
float scaleX = mDisplayWidth / mVideoWidth, scaleY = mDisplayHeight / mVideoHeight;
//proportions between screen and frame dimensions
float scale = mDisplayHeight / Math.abs(by - ay);
Matrix matrix = new Matrix();
matrix.reset();
matrix.setScale(scale / scaleX, scale / scaleY);
//scaling video
matrix.postTranslate(-scale * ax, -scale * ay);
//move video, so the needed part of it will be displayed properly
mTextureView.setLayoutParams(new FrameLayout.LayoutParams((int) mDisplayWidth, (int) mDisplayHeight));
mTextureView.setTransform(matrix);
//updating the Texture view
I need a way to scale an image down to 78x78. I have found ways of doing this by cutting part of the image off, like this:
Bitmap image = Bitmap.createBitmap(image, 0, 0, 78, 78);
but I need to maintain as much of the image as possible. I had thought of scaling the image down and then making it square:
Bitmap image = Bitmap.createScaledBitmap(imageTest, 78, 78, true);
but of course this creates a square image that is squashed.
Can anyone suggest how I can create a 78x78 image that doesn't rescale and maintains as much of the original image as possible?
From what I understood, you should scale down and center crop the image. Try this code out.
public Bitmap scaleCenterCrop(Bitmap source, int newHeight, int newWidth) {
int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight();
// Compute the scaling factors to fit the new height and width, respectively.
// To cover the final image, the final scaling will be the bigger
// of these two.
float xScale = (float) newWidth / sourceWidth;
float yScale = (float) newHeight / sourceHeight;
float scale = Math.max(xScale, yScale);
// Now get the size of the source bitmap when scaled
float scaledWidth = scale * sourceWidth;
float scaledHeight = scale * sourceHeight;
// Let's find out the upper left coordinates if the scaled bitmap
// should be centered in the new size give by the parameters
float left = (newWidth - scaledWidth) / 2;
float top = (newHeight - scaledHeight) / 2;
// The target rectangle for the new, scaled version of the source bitmap will now
// be
RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
// Finally, we create a new bitmap of the specified size and draw our new,
// scaled bitmap onto it.
Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig());
Canvas canvas = new Canvas(dest);
canvas.drawBitmap(source, null, targetRect, null);
return dest;
}
Hope it helps
Try this:
Bitmap image = Bitmap.createScaledBitmap(testImage, (int) 78 * (testImage.getWidth() / testImage.getHeight()), 78, true);
image = Bitmap.createBitmap(image, (int) (image.getWidth() - 78) / 2, 78);
Haven't tested this, as I'm on my way to bed, but it should accomplish what you want, so long as your image has a width greater than or equal to its height.
Regardless, I'd suggest you use BufferedImage instead of Bitmap.
The idea here would be resize your image using the same resize rate for width and height keeping the smaller size in 78. After that you can use a center point based crop to get the middle of your image and making it a squared image.
Image srcImage;
int widthSrc = 150;
int heightSrc = 180;
float resizeRate = 78 / min(widthSrc, heightSrc);
Image resizedImage = resizeImage($srcImage, resizeRate);
int widthDest = 78;
int heightDest = 78;
int cropX = ($widthSrc - $widthDest)/2;
int cropY = ($heightSrc - $heightDest)/2;
Image croppedImage = cropImage(resizedImage,$widthDest, $heightDest, $cropX, $cropY);
If the image is already square you can skip the crop part.
I am using the following texture
It is being displayed only partially and it gets repeated when I used the following code:
float scale = (float)( (float)Gdx.graphics.getWidth() / (float)(tex.getWidth()));
w = Gdx.graphics.getWidth();
h = scale * tex.getHeight();
float x = 0.0f;
float y = Gdx.graphics.getWidth() - h * splashTimer;
Sprite s = new Sprite(tex);
//s.setOriginCenter();
// s.setScale(scale);
//s.setOriginCenter();
// s.setOriginCenter();
//s.setSize(s.getWidth() * scale, s.getHeight() * scale);
//s.setOriginCenter();
s.setScale(0.1f);//1.0f + (1.0f - scale));
s.setOrigin(0, 0);
s.setPosition(x, y);
batch.begin();
batch.draw(s,0,0,Gdx.graphics.getWidth(), scale * s.getHeight());
// s.draw(batch);
batch.end();
It gets chopped and repeated in Android and looks like this:
Orange isn't even drawn. Why is this happening?
Apparently this happened because I was using mipmaps. I disabled the use of mipmaps and now it works.
I am creating a little game in Java and I have an image which gets rotated.
As you can see in the two images below, there is a giant ship which slowly rotates in the game, but when it gets to a certain point it gets cut off (due to its own little BufferedImage).
Heres my rendering code:
public void drawImageRotated(BufferedImage img, double x, double y, double scale, double angle) {
x -= xScroll;
y -= yScroll;
BufferedImage image = new BufferedImage((int)(img.getWidth() * 1.5D), (int)(img.getHeight() * 1.5D), 2);
Graphics2D g = (Graphics2D)image.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.rotate(Math.toRadians(angle), image.getWidth() / 2, image.getHeight() / 2);
g.drawImage(img, image.getWidth() / 2 - img.getWidth() / 2, image.getHeight() / 2 - image.getHeight() / 2, null);
g2d.drawImage(image, (int)(x-image.getWidth()*scale/2), (int)(y-image.getHeight()*scale/2), (int)(image.getWidth()*scale), (int)(image.getHeight()*scale), null);
g.dispose();
}
Back to the matter at hand, how can i work out the maximum x and y size of an image during rotation so I can compensate with my buffered images size?
If you have a basically rectangular image which is rotated around its center, the maximum width and height during rotation will be when a diagonal of the image rectangle is horizontal or vertical. This diagonal distance could be computed with the Pythagorean Theorem and used for the width and height of the BufferedImage.
int size = (int) Math.sqrt((img.getWidth() * img.getWidth()) + (img.getHeight() * img.getHeight()));
BufferedImage image = new BufferedImage(size, size, 2);
// The rest of your code as before
how can i work out the maximum x and y size of an image during rotation so I can compensate with my buffered images size?
double sin = Math.abs(Math.sin(angle));
double cos = Math.abs(Math.cos(angle));
int w = image.getWidth();
int h = image.getHeight();
int neww = (int)Math.floor(w*cos+h*sin);
int newh = (int)Math.floor(h*cos+w*sin);
The above code was taken from this example: Java(SWING) working with Rotation
An alternative is to rotate the actual Graphics object, draw the image, and restore the rotation:
AffineTransform old = g2d.getTransform();
g2d.rotate(Math.toRadians(angle), x + image.getWidth() / 2, y + image.getWidth() / 2);
g2d.drawImage(image, x, y, null);
g2d.setTransform(old);
Let's consider width being the width of the original image, height its original height and angle the rotation angle value in radians.
According to my calculations, the size of the rotated image is something like this:
rotatedWidth = Math.cos(angle) * width + Math.sin(angle) * height;
rotatedHeight = Math.sin(angle) * width + Math.cos(angle) * height;
You may also need to take a look at this thread as well, as it may help.
im trying to resize bufferdImage in memory in java but to keep the aspect ratio of the image
im have something like this but this is not good
int w = picture.getWidth();
int h = picture.getWidth();
int neww=w;
int newh=h;
int wfactor = w;
int hfactor = h;
if(w > DEFULT_PICTURE_WIDTH || h > DEFULT_PICTURE_HIGHT)
{
while(neww > DEFULT_PICTURE_WIDTH)
{
neww = wfactor /2;
newh = hfactor /2;
wfactor = neww;
hfactor = newh;
}
}
picture = Utils.resizePicture(picture,neww,newh);
Adding to Erik's point about getScaledInstance, if you moved away from it to using the recommended scaling mechanisms in Java2D, you might have noticed that your images look noticeably worse.
The reason for that is when the Java2D discouraged use of getScaledInstance and AreaAveragingScaleFilter, they didn't replace it with anything as easy to use in the API, instead we were left to our own devices using Java2D APIs directly. Fortunately, Chris Campbell (from the J2D team) followed up with the recommendation of using an incremental scaling technique that gives similar looking results to AreaAveragingScaleFilter and runs faster; unfortunately the code is of a decent size and doesn't address your original question of honoring proportions.
About 6 months ago I saw all these questions on SO again and again about "scaling images in Java" and eventually collected all the advice, did all the digging and research I could, and compiled all of into a single "best practices" image scaling library.
The API is dead simple as it is only 1 class and a bunch of static methods. Basic use looks like this:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 320);
This is the simplest call where the library will make a best-guess at the quality, honor your image proportions, and fit the result within a 320x320 bounding box. NOTE, the bounding box is just the maximum W/H used, since your image proportions are honored, the resulting image would still honor that, say 320x200.
If you want to override the automatic mode and force it to give you the best-looking result and even apply a very mild anti-alias filter to the result so it looks even better (especially good for thumbnails), that call would look like:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY,
150, 100, Scalr.OP_ANTIALIAS);
These are all just examples, the API is broad and covers everything from super-simple use cases to very specialized. You can even pass in your own BufferedImageOps to be applied to the image (and the library automatically fixes the 6-year BufferedImageOp JDK bug for you!)
There is a lot more to scaling images in Java successfully that the library does for you, for example always keeping the image in one of the best supported RGB or ARGB image types while operating on it. Under the covers the Java2D image processing pipeline falls back to an inferior software pipeline if the image type used for any image operations is poorly supported.
If all that sounded like a lot of headache, it sort of is... that's why I wrote the library and open sourced it, so folks could just resize their images and move on with their lives without needing to worry about it.
If width, height of source and target are known, use following function to determine scale of the image.
private double determineImageScale(int sourceWidth, int sourceHeight, int targetWidth, int targetHeight) {
double scalex = (double) targetWidth / sourceWidth;
double scaley = (double) targetHeight / sourceHeight;
return Math.min(scalex, scaley);
}
Then use this scale to scale up/down the image using following code
Image scaledImage = sourceBufferedImage.getScaledInstance((int) (width * scale), (int) (height * scale), Image.SCALE_SMOOTH);
For starters - take a look at line 2. Shouldnt that be getHeight()?
You dont want a while loop for the resizing, you want to find out the resizing ratio, which is a simple bit of math.
(width / height) = (new_width / new_height)
If you know one of the 'new' sizes, the other can be found via multiplication
new_height * (width / height) = new_width
You can also use the lazy method provided by BufferedImage's superclass Image, getScaledInstance() - using -1 for either width or height will maintain aspect ratio
ex:
scaledPic = picture.getScaledInstance(new_width, -1, Image.SCALE_FAST);
You may have a look at perils-of-image-getscaledinstance.html that explains why getScaledInstance(), used in some of the answers, should be avoided.
The article also provides alternative code.
I use these two methods to scale images, where max is the bigger dimension of your destination image. For 100x100 image it will be 100, for 200x300 image it will be 300.
public static BufferedImage scale(InputStream is, int max) {
Image image = null;
try {
image = ImageIO.read(is);
} catch (IOException e) {
e.printStackTrace();
}
int width = image.getWidth(null);
int height = image.getHeight(null);
double dWidth = 0;
double dHeight = 0;
if (width == height) {
dWidth = max;
dHeight = max;
}
else if (width > height) {
dWidth = max;
dHeight = ((double) height / (double) width) * max;
}
else {
dHeight = max;
dWidth = ((double) width / (double) height) * max;
}
image = image.getScaledInstance((int) dWidth, (int) dHeight, Image.SCALE_SMOOTH);
BufferedImage bImage = toBufferedImage(image);
return bImage;
}
public static BufferedImage toBufferedImage(Image img)
{
if (img instanceof BufferedImage)
{
return (BufferedImage) img;
}
BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics2D bGr = bimage.createGraphics();
bGr.drawImage(img, 0, 0, null);
bGr.dispose();
return bimage;
}
If you want to resize a picture of w0 x h0 to w1 x h1 by keeping the aspect ratio, then calculate the vertical and horizontal scale and select the smaller one.
double scalex = 1;
double scaley = 1;
if (scalingMode == ScalingMode.WINDOW_SIZE) {
scalex = (double)getWidth() / frontbuffer.getWidth();
scaley = (double)getHeight() / frontbuffer.getHeight();
} else
if (scalingMode == ScalingMode.KEEP_ASPECT) {
double sx = (double)getWidth() / frontbuffer.getWidth();
double sy = (double)getHeight() / frontbuffer.getHeight();
scalex = Math.min(sx, sy);
scaley = scalex;
// center the image
g2.translate((getWidth() - (frontbuffer.getWidth() * scalex)) / 2,
(getHeight() - (frontbuffer.getHeight() * scaley)) / 2);
}
g2.scale(scalex, scaley);
if (interpolation != ImageInterpolation.NONE) {
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolation.hint);
}
g2.drawImage(frontbuffer, 0, 0, null);
private static BufferedImage resize(BufferedImage img, int width, int height) {
double scalex = (double) width / img.getWidth();
double scaley = (double) height / img.getHeight();
double scale = Math.min(scalex, scaley);
int w = (int) (img.getWidth() * scale);
int h = (int) (img.getHeight() * scale);
Image tmp = img.getScaledInstance(w, h, Image.SCALE_SMOOTH);
BufferedImage resized = new BufferedImage(w, h, img.getType());
Graphics2D g2d = resized.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return resized;
}