javacv录制屏幕图像颜色不正确

java提供获取屏幕数据的接口,我们可以使用这个来实现截屏。

BufferedImage image = robot.createScreenCapture(screenRect);

这个接口返回的是BufferedImage对象。如果只是截屏,我们直接调用ImageIO的保存接口就可以。若需要录制屏幕,就需要结合javacv来保存视频文件。
javacv处理的图像类是Frame。工具库里面提供了BufferedImage和Frame的转换类,例如Java2DFrameConverter、Java2DFrameUtils。但是,我们直接转换后,会发现显示和保存的图片颜色是错误的。

Java2DFrameUtils.toFrame(BufferedImage src)

具体折腾过程就不说明了。我是最后分析摄像头获取的Frame格式,然后转换成BufferedImage。发现这时是没有问题的。所以,我干脆把截屏获取的BufferedImage也转换成跟摄像头一样的格式算了。结果,图片显示正常。
摄像头获取的Frame转换成BufferedImage的type是TYPE_3BYTE_BGR,而截屏获取的BufferedImage的type是TYPE_INT_RGB。初步推测,javacv的转换逻辑存在问题,导致颜色错误。对于究竟是哪个错误,我没有再去分析。

问题还没有结束!!!以上转换后获取的Frame,亮度特别大,跟实际看到的效果不一样。这个问题明显就是Gamma。所以按照Gamma优化一下就行。
对于Gamma是什么,参考以下文章:
https://blog.csdn.net/lehuoziyuan/article/details/84067207

最后把我整个实现类贴出来,具体功能都有注释,自己研究就可以。这个实现类我是按照其它Grabber接口定义的。因为拍照和录像跟截屏和录屏的区别就只有这个Grabber,所以我都是直接替换这个Grabber就弄好截屏和录屏功能。

package org.ufo.rtmp;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;

import org.bytedeco.javacv.CanvasFrame;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;

/**
 * 录制屏幕的Grabber,接口参考其它Grabber
 */
public class ScreenFrameGrabber {
	// 采集帧率,兼容其它Grabber接口用
	private double frameRate;
	// 采集器
	private Robot robot;
	// 采集区域
	private Rectangle screenRect;
	// 屏幕大小
	private Dimension screenSize;
	// 图像转换缓存
	private BufferedImage copyImage;
	// 图像转换工具(主要是调整Gamma)
	private Java2DFrameConverter converter;
	// 图像的Gamma值(1.0表示不进行校正,现在显示器使用的是2.2)
	// 这个Gamma校正,在CanvasFrame里面也有使用,可以查看源码理解
	// https://blog.csdn.net/lehuoziyuan/article/details/84067207
	// 当gamma值小于1时(一般选择1/2.2),图像的整体亮度值得到提升,同时低灰度处的对比度增加,高灰度处的对比度降低,更利于分辩低灰度值时的图像细节
	// 当gamma值大于1时(一般选择2.2),图像的整体亮度值得到减小,同时低灰度处的对比度降低,高灰度处的对比度增加,更利于分辩高灰度值时的图像细节
	private double gamma = 1.0;

	public void start() {
		// 检查屏幕设备
		GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		GraphicsDevice[] gs = ge.getScreenDevices();
		GraphicsDevice display = null;
		for (int i = 0; i < gs.length; i++) {
			System.out.println("屏幕设备:" + gs[i].getType() + ", " + gs[i].getIDstring());
			if (gs[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
				display = gs[i];
			}
		}
		if (display == null) {
			System.out.println("没有可用屏幕");
			return;
		}
		// 创建采集工具
		try {
			robot = new Robot(display);
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}
		// 显示器的Gamma值
		double g = CanvasFrame.getGamma(display);
		gamma = g == 0.0 ? 1.0 : g;
		// 获取当前屏幕大小
		screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		System.out.println("屏幕大小:" + screenSize.width + "*" + screenSize.height);
		// 默认选择左边640*480的区域
		screenRect = new Rectangle(0, 0, 640, 480);
		// 默认帧率
		frameRate = 25.0;
	}

	public Frame grab() {
		BufferedImage image = robot.createScreenCapture(screenRect);
		// 创建转换缓存,且图像类别使用TYPE_3BYTE_BGR。这是摄像头图像使用的格式
		// 默认获取的图像类别是TYPE_INT_RGB,这个直接转换后颜色会错误!
		// 另外,若对Frame使用OpenCV的图像处理接口(opencv_imgproc),一定要转换成这个类别,否则会报错!
		if (copyImage == null ||
				copyImage.getWidth() != image.getWidth() ||
				copyImage.getHeight() != image.getHeight()) {
			copyImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
		}
		// 将原图绘制到缓存上(实现拷贝和类别转换)
		Graphics g = copyImage.getGraphics();
		g.drawImage(image, 0, 0, null);
		g.dispose();
		// 创建转换工具
		if (converter == null) {
			converter = new Java2DFrameConverter();
		}
		// 由于获取的图片没有进行Gamma处理,亮度会特别高,需要进行校正
		return converter.getFrame(copyImage, gamma);
	}

	public BufferedImage grabBufferedImage() {
		return robot.createScreenCapture(screenRect);
	}

	public void close() {
	}

	public boolean hasVideo() {
		return robot != null;
	}

	public int getImageWidth() {
		return screenRect.width;
	}

	public void setImageWidth(int imageWidth) {
		screenRect.width = imageWidth;
	}

	public int getImageHeight() {
		return screenRect.height;
	}

	public void setImageHeight(int imageHeight) {
		screenRect.height = imageHeight;
	}

	public int getImageX() {
		return screenRect.x;
	}

	public void setImageX(int imageX) {
		screenRect.x = imageX;
	}

	public int getImageY() {
		return screenRect.y;
	}

	public void setImageY(int imageY) {
		screenRect.y = imageY;
	}

	public double getFrameRate() {
		return frameRate;
	}

	public void setFrameRate(double frameRate) {
		this.frameRate = frameRate;
	}

	public double getGamma() {
		return gamma;
	}

	public void setGamma(double gamma) {
		this.gamma = gamma;
	}

	public Robot getRobot() {
		return robot;
	}

	public Rectangle getScreenRect() {
		return screenRect;
	}

	public Dimension getScreenSize() {
		return screenSize;
	}
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值