javacv icon indicating copy to clipboard operation
javacv copied to clipboard

zoompan filter output video has strange color

Open zhangxueyii opened this issue 4 years ago • 11 comments

I'm trying to generate a video with ffmpeg pan effect but got a strange color in output video.

The input image is like this, a blue shoe: image

The filters is zoompan='1.5':x='if(lte(on,-1),(iw-iw/zoom)/2,x+10)':y='if(lte(on,1),(ih-ih/zoom)/2,y)'

The strange problem is: in the output video, the pan effect works well but the shoe is in different color. The shoe in the output video are in 3 different colors but as you see the shoe in input image is in blue.

image image image

When I change the filter to zoompan='1.5':x='if(lte(on,-1),(iw-iw/zoom)/2,x+3)':y='if(lte(on,1),(ih-ih/zoom)/2,y)', the only difference is x+3, the output video looks good.

Can anyone help on this issue? personally I believe the different between x+10 and x+3 is the speed the image moves from right to left, it should not affect the image color.

Thanks a lot in advance.

The demo code to reproduce this issue:

`public static void generateMp4(String outPath, int width, int height, int frameRate, File img) throws FrameRecorder.Exception { FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outPath, width, height); // set encoding recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); recorder.setFrameRate(frameRate); // Set the video data format recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); recorder.setFormat("mp4"); try { recorder.start(); // Record video int count = 0; Java2DFrameConverter converter = new Java2DFrameConverter(); BufferedImage bufferedImage = ImageIO.read(img); Frame frame = converter.getFrame(bufferedImage); String scale = width + "X" + height;

        FFmpegFrameFilter filter = new FFmpegFrameFilter(String.format("zoompan='1.5':x='if(lte(on,-1),(iw-iw/zoom)/2,x+10)':y='if(lte(on,1),(ih-ih/zoom)/2,y)':s=%s", scale), width, height);
        filter.setPixelFormat(AV_PIX_FMT_GBRP);
        filter.setImageWidth(width);
        filter.setImageHeight(height);
        filter.start();
        filter.push(frame);

        Frame filteredFrame = null;
        while ((filteredFrame = filter.pullImage()) != null) {
            recorder.record(filteredFrame, AV_PIX_FMT_GBRP);
            count++;
        }
        System.out.println("total frames " + count);
    } catch (
            IOException e) {
        e.printStackTrace();
    } finally {
        // release resources
        recorder.stop();
        recorder.release();
    }
}

`

zhangxueyii avatar May 13 '21 11:05 zhangxueyii

Make sure you're calling getPixelFormat() and using its values where appropriate.

saudet avatar May 13 '21 11:05 saudet

@saudet Thanks for the quick response. I just updated my post with full code and detailed issue. Can you help take a look?

in short, seems the x+? arguments in this filter affect the output video color but I really don't understand why.

zoompan='1.5':x='if(lte(on,-1),(iw-iw/zoom)/2,x+3)':y='if(lte(on,1),(ih-ih/zoom)/2,y)'

zhangxueyii avatar May 13 '21 11:05 zhangxueyii

The pixel formats don't look right to me. Make sure that you're giving the correct pixel formats to the push() and record() methods.

saudet avatar May 13 '21 12:05 saudet

@saudet Could you help guide me how to know the correct pixel format I should use? Here I'm reading a jpeg image and push it to a ffmpeg filter.

and, I understand the pixel format in my code AV_PIX_FMT_GBRP indeed looks a little strange, but it works perfects well when filter is zoompan='1.5':x='if(lte(on,-1),(iw-iw/zoom)/2,x+3)':y='if(lte(on,1),(ih-ih/zoom)/2,y)' but went wrong when I change x+3 to x+10. Do you know why could be the cause?

zhangxueyii avatar May 13 '21 14:05 zhangxueyii

Let's check what you get from ImageIO.read(img). Could you provide the output of bufferedImage.toString()?

saudet avatar May 14 '21 00:05 saudet

@saudet the output

BufferedImage@3bd79444: type = 5 ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace@5671078e transparency = 1 has alpha = false isAlphaPre = false ByteInterleavedRaster: width = 800 height = 800 #numDataElements 3 dataOff[0] = 2

zhangxueyii avatar May 14 '21 03:05 zhangxueyii

That's in normal BGR pixel format: https://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferedImage.html#TYPE_3BYTE_BGR Try to use the same pixel format for FFmpeg as well...

saudet avatar May 14 '21 03:05 saudet

Great, it works well when I use AV_PIX_FMT_BGR24 for push and use filter.getPixelFormat() for record method. Thanks a lot.

BTW, do you know which videocodec support which pixel format? is there any document I can refer to?

I'm using videoCodec AV_CODEC_ID_H264 when i initialize an recorder and using pixel format AV_PIX_FMT_BGR24 when pushing in a frame. I understand the pixel format AV_PIX_FMT_BGR24 is because of the image format i'm trying to load, but actually I don't understand
which videocodec is the best choice, actually I choose AV_CODEC_ID_H264 just because of it's frequently used in code examples. Can you help help me understand this?

FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outPath, width, height); // set encoding recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);

zhangxueyii avatar May 14 '21 04:05 zhangxueyii

For detailed information specific to FFmpeg, I would recommend asking upstream about that: https://ffmpeg.org/contact.html

saudet avatar May 14 '21 04:05 saudet

@saudet Great thanks for your help.

zhangxueyii avatar May 14 '21 07:05 zhangxueyii

In any case, we only need to specify the pixel format of the input. The filters and encoders convert the frames internally to whatever they need.

saudet avatar May 14 '21 07:05 saudet