drawImage error; no method found for drawImage - java

/**
* The purpose of this program is to make an image and turn it into a kaleidoscope
*
* #author (Danny Meijo)
* #version (07/27/2017)
*/
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
public class KaleidoscopeImage
{
private Picture canvas = null;
private Picture canvas2 = null;
private Picture pictureObj = null;
private Picture scaledPicture = null;
private Picture clippedPicture = null;
private Graphics g = null;
private Graphics g2 = null;
private Graphics gV2 = null;
private Graphics g2V2 = null;
private Graphics gV3 = null;
private Graphics g2V3 = null;
KaleidoscopeImage(Picture Canvas, Picture image, Picture Canvas2)
{
canvas = Canvas;
canvas2 = Canvas2;
pictureObj = image;
g = canvas.getGraphics();
g2 = (Graphics2D)g;
}
public Picture firstPanel()
{
g2.drawImage(pictureObj.getImage(), 0, canvas.getHeight() / 2, null);
Pixel bottomLeftPixel = null;
Pixel topRightPixel = null;
Color sourceColor1 = null;
for(int ty = 0, by = canvas.getHeight(); ty < canvas.getHeight() / 2; ty++, by--)
{
for(int lx = 0, rx = canvas.getWidth(); lx < canvas.getWidth() / 2; lx++, rx--)
{
bottomLeftPixel = canvas.getPixel(lx, by - 1);
sourceColor1 = bottomLeftPixel.getColor();
topRightPixel = canvas.getPixel(rx - 1, ty);
topRightPixel.setColor(sourceColor1);
}
}
Pixel sourcePixel = null;
Pixel targetPixel = null;
Color sourceColor2 = null;
Color targetColor = null;
for(int y = 0; y < canvas.getHeight() / 2; y++)
{
for(int lx = 0, rx = canvas.getWidth(); lx < canvas.getWidth() / 2; lx++, rx--)
{
sourcePixel = canvas.getPixel(rx - 1,y);
sourceColor2 = sourcePixel.getColor();
targetPixel = canvas2.getPixel(lx,y);
targetPixel.setColor(sourceColor2);
}
}
return canvas2;
}
public Picture secondPanel()
{
Pixel leftPixel = null;
Pixel rightPixel = null;
Color sourceColor = null;
for(int y = 0; y < canvas2.getHeight() / 2; y++)
{
for(int lx = 0, rx = canvas2.getWidth(); lx < canvas2.getWidth() / 2; lx++, rx--)
{
leftPixel = canvas2.getPixel(lx,y);
sourceColor = leftPixel.getColor();
rightPixel = canvas2.getPixel(rx - 1, y);
rightPixel.setColor(sourceColor);
}
}
return canvas2;
}
public Picture thirdPanel()
{
Pixel topPixel = null;
Pixel bottomPixel = null;
Color sourceColor = null;
for(int lx = 0, rx = canvas2.getWidth(); lx < canvas2.getWidth() / 2; lx++, rx--)
{
for(int ty = 0, by = canvas2.getHeight(); ty < canvas2.getHeight() / 2; ty++, by--)
{
topPixel = canvas2.getPixel(rx - 1, ty);
sourceColor = topPixel.getColor();
bottomPixel = canvas2.getPixel(rx - 1, by - 1);
bottomPixel.setColor(sourceColor);
}
}
return canvas2;
}
public Picture fourthPanel()
{
Pixel leftPixel = null;
Pixel rightPixel = null;
Color sourceColor = null;
for(int lx = 0, rx = canvas2.getWidth(); lx < canvas2.getWidth() / 2; lx++, rx--)
{
for(int ty = 0, by = canvas2.getHeight(); ty < canvas2.getHeight() / 2; ty++, by--)
{
leftPixel = canvas2.getPixel(rx - 1, by - 1);
sourceColor = leftPixel.getColor();
rightPixel = canvas2.getPixel(lx, by - 1);
rightPixel.setColor(sourceColor);
}
}
return canvas2;
}
public Picture scalePicture(double xFactor, double yFactor)
{
AffineTransform scaleTransform = new AffineTransform();
scaleTransform.scale(xFactor, yFactor);
scaledPicture = new Picture((int)(canvas2.getWidth() * xFactor), (int)(canvas2.getHeight() * yFactor));
gV2 = scaledPicture.getGraphics();
g2V2 = (Graphics2D)gV2;
g2V2.drawImage(canvas2.getImage(), scaleTransform, null);
return scaledPicture;
}
public Picture clipPicture(Color color)
{
Picture canvas3 = new Picture(canvas2.getWidth(), canvas2.getHeight());
Pixel sourcePixel = null;
Pixel targetPixel = null;
Color sourceColor = null;
Color targetColor = null;
for(int y = 0; y < canvas2.getHeight(); y++)
{
for(int x = 0; x < canvas.getWidth(); x++)
{
sourcePixel = canvas2.getPixel(x,y);
sourceColor = sourcePixel.getColor();
targetPixel = canvas3.getPixel(x,y);
targetPixel.setColor(sourceColor);
}
}
gV3 = canvas3.getGraphics();
g2V3 = (Graphics2D)gV3;
canvas3.setAllPixelsToAColor(color);
Ellipse2D.Double clip = new Ellipse2D.Double(0,0, canvas3.getHeight(), canvas3.getWidth());
g2V3.setClip(clip);
g2V3.drawImage(canvas2.getImage(), 0, 0, canvas3.getHeight(), canvas3.getWidth(), null);
return canvas3;
}
}
Sorry, this is my first post, and I am also very new to java, since I'm learning it over the summer. I was not sure how to cut it to just the parts that I need, but the problem I'm having is in the scalePicture method. I was copying what I saw in a demo program to scale the image down to 0.75x0.75. But, in my program, there is an error with the drawImage method, where as the demo progam had no error.
If you are curious this is the demo that I was copying:
import java.awt.geom.AffineTransform;
import java.awt.Graphics;
import java.awt.Graphics2D;
class ScalingDemo
{
private Picture originalPicture = null;
private Picture newPicture = null;
private Graphics g = null;
private Graphics2D g2 = null;
ScalingDemo(Picture pic)
{
originalPicture = pic;
}
public Picture scalePicture(double xFactor, double yFactor)
{
AffineTransform scaleTransform = new AffineTransform();
scaleTransform.scale(xFactor, yFactor);
newPicture = new Picture((int)(originalPicture.getWidth()*xFactor), (int)(originalPicture.getHeight()*yFactor));
g = newPicture.getGraphics();
g2 = (Graphics2D)g;
g2.drawImage(originalPicture.getImage(), scaleTransform, null);
return newPicture;
}
}

Looks like the error is at line:
g2V2.drawImage(canvas2.getImage(), scaleTransform, null); - there's no such method in java.awt.Graphics interface.
You should use method with another signature:
drawImage(Image img, int x, int y, ImageObserver observer) - see here

Related

How to Place Bitmaps Vertically

I've got some code from user xil3 where it merges the bitmaps horizontally. Does anyone know how I can make it to do it vertically instead?
public Bitmap combineImages(Bitmap c, Bitmap s, String loc) { // can add a 3rd parameter 'String loc' if you want to save the new image - left some code to do that at the bottom
Bitmap cs = null;
int width, height = 0;
if (c.getHeight() > s.getHeight()) {
width = c.getWidth() + s.getWidth();
height = c.getHeight();
} else {
width = c.getWidth() + s.getWidth();
height = s.getHeight();
}
cs = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
Canvas comboImage = new Canvas(cs);
comboImage.drawBitmap(c, 0f, 0f, null);
comboImage.drawBitmap(s, c.getHeight(), 0f, null);
// this is an extra bit I added, just incase you want to save the new image somewhere and then return the location
String tmpImg = String.valueOf(System.currentTimeMillis()) + ".png";
OutputStream os = null;
try {
os = new FileOutputStream(loc + tmpImg);
cs.compress(Bitmap.CompressFormat.PNG, 100, os);
} catch (IOException e) {
Log.e("combineImages", "problem combining images", e);
}
return cs;
}
I ended up finding a new piece of code here which saves them vertically-
private Bitmap mergeMultiple(ArrayList<Bitmap> parts) {
int w = 0, h = 0;
for (int i = 0; i < parts.size(); i++) {
if (i < parts.size() - 1) {
w = parts.get(i).getWidth() > parts.get(i + 1).getWidth() ? parts.get(i).getWidth() : parts.get(i + 1).getWidth();
}
h += parts.get(i).getHeight();
}
Bitmap temp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(temp);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
int top = 0;
for (int i = 0; i < parts.size(); i++) {
top = (i == 0 ? 0 : top + parts.get(i).getHeight() + 100);
canvas.drawBitmap(parts.get(i), 0f, top,paint );
}
return temp;
}

LibGDX applyLinearImpulse trying to implement Gravity....Confused

Street Fighter 2d clone I'm trying to get the player Ken to come back down after jumping. He just goes higher and higher but I want it to be like gravity so he's pulled back down. Then I will set a ground level where he cant pass through.
I've researched the applyLinearImpulse method to be called on the body which is initialised beforre asa I kept getting null pointer exception it seems that now it don't crash but Ken just gets drawn further up the Y Axis. Its left me very confused.
Any advice links greatly appreciated.
Ken class
public class Ken extends Player {
private static final int FRAME_COLS = 6, FRAME_ROWS = 1;
private static final int COLUMNS_KICK = 6;
private static final int COLUMNS_LEFT = 8;
private static final int COLUMNS_RIGHT = 8;
private static final int COLUMNS_JUMP = 10;
private static final int COLUMNS_PUNCH = 6;
private static final int COLUMNS_FRONTFLIP = 8;
private static final int COLUMNS_BACKFLIP = 8;
public static final int FRAME_FRONTFLIP = 1;
public static final int FRAME_BACKLIP = 1;
float x, y;
Animation<TextureRegion> walkAnimation;
Animation<TextureRegion> kickAnimation;
Animation<TextureRegion> punchAnimation;
Animation<TextureRegion> leftAnimation;
Animation<TextureRegion> rightAnimation;
Animation<TextureRegion> jumpAnimation;
Animation<TextureRegion> frontFlipAnimation;
Animation<TextureRegion> backFlipAnimation;
Texture walkSheet;
Texture kickSheet;
Texture punchSheet;
Texture leftSheet;
Texture rightSheet;
Texture jumpSheet;
Texture frontFlipSheet;
Texture backFlipSheet;
public Body body;
public World world;
boolean alive = true;
private final static int STARTING_X = 50;
private final static int STARTING_Y = 30;
TextureRegion reg;
float stateTime;
public Ken(GameScreen screen){
this.world = screen.getWorld();
defineKen();
createIdleAnimation();
kickAnimation();
punchAnimation();
lefttAnimation();
righttAnimation();
jumpAnimation();
frontFlipAnimation();
backFlipAnimation();
this.setPosition(STARTING_X, STARTING_Y);
}
public void createIdleAnimation() {
walkSheet = new Texture(Gdx.files.internal("ken/idle.png"));
TextureRegion[][] tmp = TextureRegion.split(walkSheet,
walkSheet.getWidth() / FRAME_COLS,
walkSheet.getHeight() / FRAME_ROWS);
TextureRegion[] walkFrames = new TextureRegion[FRAME_COLS * FRAME_ROWS];
int index = 0;
for (int i = 0; i < FRAME_ROWS; i++) {
for (int j = 0; j < FRAME_COLS; j++) {
walkFrames[index++] = tmp[i][j];
}
}
walkAnimation = new Animation<TextureRegion>(0.1f, walkFrames);
stateTime = 0f;
reg=walkAnimation.getKeyFrame(0);
}
public void kickAnimation(){
kickSheet = new Texture(Gdx.files.internal("ken/kick_low.png"));
TextureRegion [][] tmp = TextureRegion.split(kickSheet, kickSheet.getWidth() / COLUMNS_KICK,
kickSheet.getHeight() / FRAME_ROWS);
TextureRegion[] kickFrames = new TextureRegion[COLUMNS_KICK * FRAME_ROWS];
int index = 0;
for (int i = 0; i < FRAME_ROWS; i++) {
for (int j = 0; j < FRAME_COLS; j++) {
kickFrames[index++] = tmp[i][j];
}
}
kickAnimation = new Animation<TextureRegion>(8f, kickFrames);
stateTime = 6f;
reg = kickAnimation.getKeyFrame(1);
}
public void lefttAnimation(){
leftSheet = new Texture(Gdx.files.internal("ken/parry_b.png"));
TextureRegion [][] tmp = TextureRegion.split(leftSheet, leftSheet.getWidth() / COLUMNS_LEFT,
leftSheet.getHeight() / FRAME_ROWS);
TextureRegion[] leftFrames = new TextureRegion[COLUMNS_LEFT * FRAME_ROWS];
int index = 0;
for (int i = 0; i < FRAME_ROWS; i++) {
for (int j = 0; j < COLUMNS_LEFT; j++) {
leftFrames[index++] = tmp[i][j];
}
}
leftAnimation = new Animation<TextureRegion>(0.1f, leftFrames);
stateTime = 0f;
reg = punchAnimation.getKeyFrame(0);
}
public void righttAnimation(){
rightSheet = new Texture(Gdx.files.internal("ken/parry_f.png"));
TextureRegion [][] tmp = TextureRegion.split(rightSheet, rightSheet.getWidth() / COLUMNS_RIGHT,
rightSheet.getHeight() / FRAME_ROWS);
TextureRegion[] rightFrames = new TextureRegion[COLUMNS_RIGHT * FRAME_ROWS];
int index = 0;
for (int i = 0; i < FRAME_ROWS; i++) {
for (int j = 0; j < COLUMNS_RIGHT; j++) {
rightFrames[index++] = tmp[i][j];
}
}
rightAnimation = new Animation<TextureRegion>(0.1f, rightFrames);
stateTime = 0f;
reg = rightAnimation.getKeyFrame(0);
}
public void punchAnimation(){
punchSheet = new Texture(Gdx.files.internal("ken/punch.png"));
TextureRegion [][] tmp = TextureRegion.split(punchSheet, punchSheet.getWidth() / COLUMNS_PUNCH,
punchSheet.getHeight() / FRAME_ROWS);
TextureRegion[] punchFrames = new TextureRegion[COLUMNS_PUNCH * FRAME_ROWS];
int index = 0;
for (int i = 0; i < FRAME_ROWS; i++) {
for (int j = 0; j < COLUMNS_PUNCH; j++) {
punchFrames[index++] = tmp[i][j];
}
}
punchAnimation = new Animation<TextureRegion>(0.1f, punchFrames);
stateTime = 0f;
reg = punchAnimation.getKeyFrame(0);
}
public void jumpAnimation(){
jumpSheet = new Texture(Gdx.files.internal("ken/jump.png"));
TextureRegion [][] tmp = TextureRegion.split(jumpSheet, jumpSheet.getWidth() / COLUMNS_JUMP,
jumpSheet.getHeight() / FRAME_ROWS);
TextureRegion[] jumpFrames = new TextureRegion[COLUMNS_JUMP * FRAME_ROWS];
int index = 0;
for (int i = 0; i < FRAME_ROWS; i++) {
for (int j = 0; j < COLUMNS_JUMP; j++) {
jumpFrames[index++] = tmp[i][j];
}
}
jumpAnimation = new Animation<TextureRegion>(0.1f, jumpFrames);
stateTime = 0f;
reg = jumpAnimation.getKeyFrame(0);
}
public void frontFlipAnimation(){
frontFlipSheet = new Texture(Gdx.files.internal("ken/front_flip.png"));
TextureRegion [][] tmp = TextureRegion.split(frontFlipSheet, frontFlipSheet.getWidth() / COLUMNS_FRONTFLIP,
frontFlipSheet.getHeight() / FRAME_ROWS);
TextureRegion[] frontFlipFrames = new TextureRegion[COLUMNS_FRONTFLIP * FRAME_FRONTFLIP];
int index = 0;
for (int i = 0; i < FRAME_FRONTFLIP; i++) {
for (int j = 0; j < COLUMNS_FRONTFLIP; j++) {
frontFlipFrames[index++] = tmp[i][j];
}
}
frontFlipAnimation = new Animation<TextureRegion>(0.1f, frontFlipFrames);
stateTime = 0f;
reg = frontFlipAnimation.getKeyFrame(0);
}
public void backFlipAnimation(){
backFlipSheet = new Texture(Gdx.files.internal("ken/back_flip.png"));
TextureRegion [][] tmp = TextureRegion.split(backFlipSheet, backFlipSheet.getWidth() / COLUMNS_BACKFLIP,
backFlipSheet.getHeight() / FRAME_BACKLIP);
TextureRegion[] backFlipFrames = new TextureRegion[COLUMNS_BACKFLIP * FRAME_BACKLIP];
int index = 0;
for (int i = 0; i < FRAME_BACKLIP; i++) {
for (int j = 0; j < COLUMNS_BACKFLIP; j++) {
backFlipFrames[index++] = tmp[i][j];
}
}
backFlipAnimation = new Animation<TextureRegion>(0.1f, backFlipFrames);
stateTime = 0f;
reg = backFlipAnimation.getKeyFrame(0);
}
#Override
public void act(float delta) {
super.act(delta);
stateTime += delta;
stateTime += delta;
reg = walkAnimation.getKeyFrame(stateTime,true);
if(Gdx.input.isKeyPressed(Input.Keys.A)){
reg = kickAnimation.getKeyFrame(stateTime, false);
this.addAction(Actions.moveTo(getX() +2, getY(), 1 / 10F));
}
if(Gdx.input.isKeyPressed(Input.Keys.S)){
reg = punchAnimation.getKeyFrame(stateTime, false);
}
if(Gdx.input.isKeyPressed(Input.Keys.LEFT)){
reg = leftAnimation.getKeyFrame(stateTime, true);
this.addAction(Actions.moveTo(getX() - 10, getY(), 1 / 10f ));
}
if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)){
reg = rightAnimation.getKeyFrame(stateTime, true);
this.addAction(Actions.moveTo(getX() + 10, getY(), 1 /10f));
}
if(Gdx.input.isKeyPressed(Input.Keys.UP)){
body.applyLinearImpulse(new Vector2(0, 20), body.getWorldCenter(), true);
reg = jumpAnimation.getKeyFrame(stateTime, false);
this.addAction(Actions.moveTo(getX(), getY() + 10, 1/ 10f));
}
if(Gdx.input.isKeyPressed(Input.Keys.D)){
reg = frontFlipAnimation.getKeyFrame(stateTime, true);
this.addAction(Actions.moveTo(getX() + 5, getY(), 1 / 10f));
}
if(Gdx.input.isKeyPressed(Input.Keys.W)){
reg = backFlipAnimation.getKeyFrame(stateTime, true);
this.addAction(Actions.moveTo(getX() - 5, getY(), 1 / 10F));
}
}
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
batch.draw(reg,getX(),getY(),getWidth()/2,getHeight()/2,getWidth(),getHeight(),getScaleX(),getScaleY(),getRotation());
}
private void defineKen(){
BodyDef bdef = new BodyDef();
bdef.position.set(32 / 100, 32 / 100);
bdef.type = BodyDef.BodyType.DynamicBody;
body = world.createBody(bdef);
FixtureDef fdef = new FixtureDef();
CircleShape shape = new CircleShape();
shape.setRadius(7 / 100);
fdef.shape = shape;
body.createFixture(fdef).setUserData(this);
body.createFixture(fdef).setUserData(this);
}
}
Repo
Thanks alot
You work with Box2d and Box2d is a 2d physics engine so it also integrated gravity.
First, when you create your World you can define the gravity force:
world = new World(new Vector2(0,-15f), true);
This will pull your Player down.
Then you must update the world every frame. So call in the render() method:
world.step(delta, 6, 2);
Now the Body position will be calculated by the world and gravity apply to the body.
Important now is that you first have a Static body as Ground otherwise, the body will fall down infinitely. Secondly, you must change the place where you draw the Image of Ken to the place of the Body so in act() method update the position:
setX(body.getPosition().x);
setY(body.getPosition().y);
Maybe you look for some Box2d tutorials to have a better understanding:
https://www.gamedevelopment.blog/full-libgdx-game-tutorial-box2d/

Java OpenCV Layer small image onto larger image with transparency

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);
}
}
}

Sobel Filter not functioning correctly

I wrote a class for Sobel operator for edge detection, but when I use an example image, my edges are off. Greatly appreciate it if someone can help me with this.
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.Raster;
import java.util.Arrays;
class SobelFilter {
private static final float[] sobel1 = { 1.0f, 0.0f, -1.0f};
private static final float[] sobel2 = { 1.0f, 2.0f, 1.0f};
private static final boolean[] sobelBoolean = {true, false};
private SobelFilter() {}
private static ConvolveOp getSobelX(boolean fs) {
Kernel kernel = null;
if (fs) {
kernel = new Kernel(1, 3, sobel1);
}
else {
kernel = new Kernel(3, 1, sobel2);
}
return new ConvolveOp(kernel, ConvolveOp.EDGE_ZERO_FILL, null);
}
private static ConvolveOp getSobelY(boolean fs) {
Kernel kernel = null;
if (fs) {
kernel = new Kernel(1, 3, sobel2);
}
else {
kernel = new Kernel(3, 1, sobel1);
}
return new ConvolveOp(kernel, ConvolveOp.EDGE_ZERO_FILL, null);
}
public static BufferedImage getSobelFilter(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
int size = width * height;
int[] x = new int[size];
int[] y = new int[size];
int[] pixelM = new int[size];
//double[] pixelD = new double[size];
BufferedImage sobelX = null;
BufferedImage sobelY = null;
for(boolean i : sobelBoolean) {
sobelX = getSobelX(i).filter(img, null);
sobelY = getSobelY(i).filter(img, null);
}
sobelX.getRaster().getPixels(0, 0, width, height, x);
sobelY.getRaster().getPixels(0, 0, width, height, y);
for(int i = 0; i < size; i++) {
pixelM[i] = (int) Math.hypot(x[i], y[i]);
//pixelD[i] = Math.atan2((double) y[i], (double) x[i]);
}
BufferedImage result =
new BufferedImage(width, height,
BufferedImage.TYPE_BYTE_GRAY);
result.getRaster().setPixels(0, 0, width, height, pixelM);
return result;
}
}
I used the valve picture from Wikipedia as an example.
Original test image
Expected result
Actual result
What you plotted is the Y component of the gradient. Consider this:
g2.drawImage(sobelX, null, 0, 0);
g2.drawImage(sobelY, null, 0, 0);
sobelX is hidden behind sobelY, so you only see the latter.
What you want is the norm of the gradient. You'll have to scan both images and calculate z = sqrt(x*x + y*y) for each pixel x of sobelX and its corresponding y of sobelY.
Pseudocode:
norm = image of the same size of sobelX and sobelY;
for (int x = 0; x < horizontal dimension of the image; ++x) {
for (int y = 0; y < vertical dimension of the image; ++y) {
xPixel = sobelX.pixelAt(x,y);
yPixel = sobelY.pixelAt(x,y);
norm.pixelAt(x,y) = Math.hypot(xPixel, yPixel);
}
}
return norm;

Convert each animated GIF frame to a separate BufferedImage

I want to be able to take an animated GIF as input, count the frames (and perhaps other metadata), and convert each to a BufferedImage.
How can I do this?
If you want all the frames to be the same size (for optimized GIFs) try something like this:
try {
String[] imageatt = new String[]{
"imageLeftPosition",
"imageTopPosition",
"imageWidth",
"imageHeight"
};
ImageReader reader = (ImageReader)ImageIO.getImageReadersByFormatName("gif").next();
ImageInputStream ciis = ImageIO.createImageInputStream(new File("house2.gif"));
reader.setInput(ciis, false);
int noi = reader.getNumImages(true);
BufferedImage master = null;
for (int i = 0; i < noi; i++) {
BufferedImage image = reader.read(i);
IIOMetadata metadata = reader.getImageMetadata(i);
Node tree = metadata.getAsTree("javax_imageio_gif_image_1.0");
NodeList children = tree.getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
Node nodeItem = children.item(j);
if(nodeItem.getNodeName().equals("ImageDescriptor")){
Map<String, Integer> imageAttr = new HashMap<String, Integer>();
for (int k = 0; k < imageatt.length; k++) {
NamedNodeMap attr = nodeItem.getAttributes();
Node attnode = attr.getNamedItem(imageatt[k]);
imageAttr.put(imageatt[k], Integer.valueOf(attnode.getNodeValue()));
}
if(i==0){
master = new BufferedImage(imageAttr.get("imageWidth"), imageAttr.get("imageHeight"), BufferedImage.TYPE_INT_ARGB);
}
master.getGraphics().drawImage(image, imageAttr.get("imageLeftPosition"), imageAttr.get("imageTopPosition"), null);
}
}
ImageIO.write(master, "GIF", new File( i + ".gif"));
}
} catch (IOException e) {
e.printStackTrace();
}
None of the answers here are correct and suitable for animation. There are many problems in each solution so I wrote something that actually works with all gif files. For instance, this takes into account the actual width and height of the image instead of taking the width and height of the first frame assuming it will fill the entire canvas, no, unfortunately it's not that simple. Second, this doesn't leave any transparent pickles. Third, this takes into account disposal Methods. Fourth, this gives you delays between frames (* 10 if you want to use it in Thread.sleep()).
private ImageFrame[] readGif(InputStream stream) throws IOException{
ArrayList<ImageFrame> frames = new ArrayList<ImageFrame>(2);
ImageReader reader = (ImageReader) ImageIO.getImageReadersByFormatName("gif").next();
reader.setInput(ImageIO.createImageInputStream(stream));
int lastx = 0;
int lasty = 0;
int width = -1;
int height = -1;
IIOMetadata metadata = reader.getStreamMetadata();
Color backgroundColor = null;
if(metadata != null) {
IIOMetadataNode globalRoot = (IIOMetadataNode) metadata.getAsTree(metadata.getNativeMetadataFormatName());
NodeList globalColorTable = globalRoot.getElementsByTagName("GlobalColorTable");
NodeList globalScreeDescriptor = globalRoot.getElementsByTagName("LogicalScreenDescriptor");
if (globalScreeDescriptor != null && globalScreeDescriptor.getLength() > 0){
IIOMetadataNode screenDescriptor = (IIOMetadataNode) globalScreeDescriptor.item(0);
if (screenDescriptor != null){
width = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenWidth"));
height = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenHeight"));
}
}
if (globalColorTable != null && globalColorTable.getLength() > 0){
IIOMetadataNode colorTable = (IIOMetadataNode) globalColorTable.item(0);
if (colorTable != null) {
String bgIndex = colorTable.getAttribute("backgroundColorIndex");
IIOMetadataNode colorEntry = (IIOMetadataNode) colorTable.getFirstChild();
while (colorEntry != null) {
if (colorEntry.getAttribute("index").equals(bgIndex)) {
int red = Integer.parseInt(colorEntry.getAttribute("red"));
int green = Integer.parseInt(colorEntry.getAttribute("green"));
int blue = Integer.parseInt(colorEntry.getAttribute("blue"));
backgroundColor = new Color(red, green, blue);
break;
}
colorEntry = (IIOMetadataNode) colorEntry.getNextSibling();
}
}
}
}
BufferedImage master = null;
boolean hasBackround = false;
for (int frameIndex = 0;; frameIndex++) {
BufferedImage image;
try{
image = reader.read(frameIndex);
}catch (IndexOutOfBoundsException io){
break;
}
if (width == -1 || height == -1){
width = image.getWidth();
height = image.getHeight();
}
IIOMetadataNode root = (IIOMetadataNode) reader.getImageMetadata(frameIndex).getAsTree("javax_imageio_gif_image_1.0");
IIOMetadataNode gce = (IIOMetadataNode) root.getElementsByTagName("GraphicControlExtension").item(0);
NodeList children = root.getChildNodes();
int delay = Integer.valueOf(gce.getAttribute("delayTime"));
String disposal = gce.getAttribute("disposalMethod");
if (master == null){
master = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
master.createGraphics().setColor(backgroundColor);
master.createGraphics().fillRect(0, 0, master.getWidth(), master.getHeight());
hasBackround = image.getWidth() == width && image.getHeight() == height;
master.createGraphics().drawImage(image, 0, 0, null);
}else{
int x = 0;
int y = 0;
for (int nodeIndex = 0; nodeIndex < children.getLength(); nodeIndex++){
Node nodeItem = children.item(nodeIndex);
if (nodeItem.getNodeName().equals("ImageDescriptor")){
NamedNodeMap map = nodeItem.getAttributes();
x = Integer.valueOf(map.getNamedItem("imageLeftPosition").getNodeValue());
y = Integer.valueOf(map.getNamedItem("imageTopPosition").getNodeValue());
}
}
if (disposal.equals("restoreToPrevious")){
BufferedImage from = null;
for (int i = frameIndex - 1; i >= 0; i--){
if (!frames.get(i).getDisposal().equals("restoreToPrevious") || frameIndex == 0){
from = frames.get(i).getImage();
break;
}
}
{
ColorModel model = from.getColorModel();
boolean alpha = from.isAlphaPremultiplied();
WritableRaster raster = from.copyData(null);
master = new BufferedImage(model, raster, alpha, null);
}
}else if (disposal.equals("restoreToBackgroundColor") && backgroundColor != null){
if (!hasBackround || frameIndex > 1){
master.createGraphics().fillRect(lastx, lasty, frames.get(frameIndex - 1).getWidth(), frames.get(frameIndex - 1).getHeight());
}
}
master.createGraphics().drawImage(image, x, y, null);
lastx = x;
lasty = y;
}
{
BufferedImage copy;
{
ColorModel model = master.getColorModel();
boolean alpha = master.isAlphaPremultiplied();
WritableRaster raster = master.copyData(null);
copy = new BufferedImage(model, raster, alpha, null);
}
frames.add(new ImageFrame(copy, delay, disposal, image.getWidth(), image.getHeight()));
}
master.flush();
}
reader.dispose();
return frames.toArray(new ImageFrame[frames.size()]);
}
And the ImageFrame class:
import java.awt.image.BufferedImage;
public class ImageFrame {
private final int delay;
private final BufferedImage image;
private final String disposal;
private final int width, height;
public ImageFrame (BufferedImage image, int delay, String disposal, int width, int height){
this.image = image;
this.delay = delay;
this.disposal = disposal;
this.width = width;
this.height = height;
}
public ImageFrame (BufferedImage image){
this.image = image;
this.delay = -1;
this.disposal = null;
this.width = -1;
this.height = -1;
}
public BufferedImage getImage() {
return image;
}
public int getDelay() {
return delay;
}
public String getDisposal() {
return disposal;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
Right, I have never done anything even slightly like this before, but a bit of Googling and fiddling in Java got me this:
public ArrayList<BufferedImage> getFrames(File gif) throws IOException{
ArrayList<BufferedImage> frames = new ArrayList<BufferedImage>();
ImageReader ir = new GIFImageReader(new GIFImageReaderSpi());
ir.setInput(ImageIO.createImageInputStream(gif));
for(int i = 0; i < ir.getNumImages(true); i++)
frames.add(ir.getRawImageType(i).createBufferedImage(ir.getWidth(i), ir.getHeight(i)));
return frames;
}
Edit: see Ansel Zandegran's modification to my answer.
To split an animated GIF into separate BufferedImage frames:
try {
ImageReader reader = ImageIO.getImageReadersByFormatName("gif").next();
File input = new File("input.gif");
ImageInputStream stream = ImageIO.createImageInputStream(input);
reader.setInput(stream);
int count = reader.getNumImages(true);
for (int index = 0; index < count; index++) {
BufferedImage frame = reader.read(index);
// Here you go
}
} catch (IOException ex) {
// An I/O problem has occurred
}
Alex's answer covers most cases, but it does have a couple of problems. It doesn't handle transparency correctly (at least according to common convention) and it is applying the current frame's disposal method to the previous frame which is incorrect. Here's a version that does handle those cases correctly:
private ImageFrame[] readGIF(ImageReader reader) throws IOException {
ArrayList<ImageFrame> frames = new ArrayList<ImageFrame>(2);
int width = -1;
int height = -1;
IIOMetadata metadata = reader.getStreamMetadata();
if (metadata != null) {
IIOMetadataNode globalRoot = (IIOMetadataNode) metadata.getAsTree(metadata.getNativeMetadataFormatName());
NodeList globalScreenDescriptor = globalRoot.getElementsByTagName("LogicalScreenDescriptor");
if (globalScreenDescriptor != null && globalScreenDescriptor.getLength() > 0) {
IIOMetadataNode screenDescriptor = (IIOMetadataNode) globalScreenDescriptor.item(0);
if (screenDescriptor != null) {
width = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenWidth"));
height = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenHeight"));
}
}
}
BufferedImage master = null;
Graphics2D masterGraphics = null;
for (int frameIndex = 0;; frameIndex++) {
BufferedImage image;
try {
image = reader.read(frameIndex);
} catch (IndexOutOfBoundsException io) {
break;
}
if (width == -1 || height == -1) {
width = image.getWidth();
height = image.getHeight();
}
IIOMetadataNode root = (IIOMetadataNode) reader.getImageMetadata(frameIndex).getAsTree("javax_imageio_gif_image_1.0");
IIOMetadataNode gce = (IIOMetadataNode) root.getElementsByTagName("GraphicControlExtension").item(0);
int delay = Integer.valueOf(gce.getAttribute("delayTime"));
String disposal = gce.getAttribute("disposalMethod");
int x = 0;
int y = 0;
if (master == null) {
master = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
masterGraphics = master.createGraphics();
masterGraphics.setBackground(new Color(0, 0, 0, 0));
} else {
NodeList children = root.getChildNodes();
for (int nodeIndex = 0; nodeIndex < children.getLength(); nodeIndex++) {
Node nodeItem = children.item(nodeIndex);
if (nodeItem.getNodeName().equals("ImageDescriptor")) {
NamedNodeMap map = nodeItem.getAttributes();
x = Integer.valueOf(map.getNamedItem("imageLeftPosition").getNodeValue());
y = Integer.valueOf(map.getNamedItem("imageTopPosition").getNodeValue());
}
}
}
masterGraphics.drawImage(image, x, y, null);
BufferedImage copy = new BufferedImage(master.getColorModel(), master.copyData(null), master.isAlphaPremultiplied(), null);
frames.add(new ImageFrame(copy, delay, disposal));
if (disposal.equals("restoreToPrevious")) {
BufferedImage from = null;
for (int i = frameIndex - 1; i >= 0; i--) {
if (!frames.get(i).getDisposal().equals("restoreToPrevious") || frameIndex == 0) {
from = frames.get(i).getImage();
break;
}
}
master = new BufferedImage(from.getColorModel(), from.copyData(null), from.isAlphaPremultiplied(), null);
masterGraphics = master.createGraphics();
masterGraphics.setBackground(new Color(0, 0, 0, 0));
} else if (disposal.equals("restoreToBackgroundColor")) {
masterGraphics.clearRect(x, y, image.getWidth(), image.getHeight());
}
}
reader.dispose();
return frames.toArray(new ImageFrame[frames.size()]);
}
private class ImageFrame {
private final int delay;
private final BufferedImage image;
private final String disposal;
public ImageFrame(BufferedImage image, int delay, String disposal) {
this.image = image;
this.delay = delay;
this.disposal = disposal;
}
public BufferedImage getImage() {
return image;
}
public int getDelay() {
return delay;
}
public String getDisposal() {
return disposal;
}
}
There is a good description of how GIF animations work in this ImageMagick tutorial.
I wrote a GIF image decoder on my own and released it under the Apache License 2.0 on GitHub. You can download it here: https://github.com/DhyanB/Open-Imaging. Example usage:
void example(final byte[] data) throws Exception {
final GifImage gif = GifDecoder .read(data);
final int width = gif.getWidth();
final int height = gif.getHeight();
final int background = gif.getBackgroundColor();
final int frameCount = gif.getFrameCount();
for (int i = 0; i < frameCount; i++) {
final BufferedImage img = gif.getFrame(i);
final int delay = gif.getDelay(i);
ImageIO.write(img, "png", new File(OUTPATH + "frame_" + i + ".png"));
}
}
The decoder supports GIF87a, GIF89a, animation, transparency and interlacing. Frames will have the width and height of the image itself and be placed on the correct position on the canvas. It respects frame transparency and disposal methods. Checkout the project description for more details such as the handling of background colors.
Additionally, the decoder doesn't suffer from this ImageIO bug: ArrayIndexOutOfBoundsException: 4096 while reading gif file.
I'd be happy to get some feedback. I've been testing with a representive set of images, however, some real field testing would be good.
Using c24w's solution, replace:
frames.add(ir.getRawImageType(i).createBufferedImage(ir.getWidth(i), ir.getHeight(i)));
With:
frames.add(ir.read(i));

Categories