Преобразование BufferedImage в Mat в opencv
Как я могу преобразовать BufferedImage в коврик в OpenCV? Я использую Java-оболочку для OpenCV (не JavaCV). Поскольку я новичок в OpenCV, у меня есть некоторые проблемы с пониманием того, как работает Mat.
Я хочу сделать что-то подобное. (На основе ответа Теда У.):
BufferedImage image = ImageIO.read(b.getClass().getResource("Lena.png"));
int rows = image.getWidth();
int cols = image.getHeight();
int type = CvType.CV_16UC1;
Mat newMat = new Mat(rows,cols,type);
for(int r=0; r<rows; r++){
for(int c=0; c<cols; c++){
newMat.put(r, c, image.getRGB(r, c));
}
}
Highgui.imwrite("Lena_copy.png",newMat);
Это не работает "Lena_copy.png" - просто черная картинка с правильными размерами.
7 ответов
Я также пытался сделать то же самое из-за необходимости объединения изображений, обработанных двумя библиотеками. И то, что я пытался сделать, это поставить byte[]
в к Mat
вместо значения RGB. И это сработало! Итак, что я сделал:
1.Converted BufferedImage
байтовый массив с:
byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
2. Затем вы можете просто поместить его в Mat, если вы установите тип CV_8UC3
image_final.put(0, 0, pixels);
Изменить: Также вы можете попробовать сделать обратное, как в этом ответе
Не хотите иметь дело с массивом больших пикселей? Просто используйте это
BufferedImage to Mat
public static Mat BufferedImage2Mat(BufferedImage image) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", byteArrayOutputStream);
byteArrayOutputStream.flush();
return Imgcodecs.imdecode(new MatOfByte(byteArrayOutputStream.toByteArray()), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
}
Мат для BufferedImage
public static BufferedImage Mat2BufferedImage(Mat matrix)throws IOException {
MatOfByte mob=new MatOfByte();
Imgcodecs.imencode(".jpg", matrix, mob);
return ImageIO.read(new ByteArrayInputStream(mob.toArray()));
}
Обратите внимание, хотя это очень незначительно. Тем не менее, таким образом, вы можете получить надежное решение, но оно использует кодирование + декодирование. Таким образом, вы теряете некоторую производительность. Обычно это от 10 до 20 миллисекунд. Кодирование JPG теряет качество изображения, а также медленно (может занять от 10 до 20 мс). BMP работает без потерь и быстро (1 или 2 мс), но требует немного больше памяти (незначительно). PNG без потерь, но немного больше времени для кодирования, чем BMP. Использование BMP должно соответствовать большинству случаев, я думаю.
Этот работал хорошо для меня, и это занимает от 0 до 1 мсек.
public static Mat bufferedImageToMat(BufferedImage bi) {
Mat mat = new Mat(bi.getHeight(), bi.getWidth(), CvType.CV_8UC3);
byte[] data = ((DataBufferByte) bi.getRaster().getDataBuffer()).getData();
mat.put(0, 0, data);
return mat;
}
Я использую следующий код в моей программе.
protected Mat img2Mat(BufferedImage in) {
Mat out;
byte[] data;
int r, g, b;
if (in.getType() == BufferedImage.TYPE_INT_RGB) {
out = new Mat(in.getHeight(), in.getWidth(), CvType.CV_8UC3);
data = new byte[in.getWidth() * in.getHeight() * (int) out.elemSize()];
int[] dataBuff = in.getRGB(0, 0, in.getWidth(), in.getHeight(), null, 0, in.getWidth());
for (int i = 0; i < dataBuff.length; i++) {
data[i * 3] = (byte) ((dataBuff[i] >> 0) & 0xFF);
data[i * 3 + 1] = (byte) ((dataBuff[i] >> 8) & 0xFF);
data[i * 3 + 2] = (byte) ((dataBuff[i] >> 16) & 0xFF);
}
} else {
out = new Mat(in.getHeight(), in.getWidth(), CvType.CV_8UC1);
data = new byte[in.getWidth() * in.getHeight() * (int) out.elemSize()];
int[] dataBuff = in.getRGB(0, 0, in.getWidth(), in.getHeight(), null, 0, in.getWidth());
for (int i = 0; i < dataBuff.length; i++) {
r = (byte) ((dataBuff[i] >> 0) & 0xFF);
g = (byte) ((dataBuff[i] >> 8) & 0xFF);
b = (byte) ((dataBuff[i] >> 16) & 0xFF);
data[i] = (byte) ((0.21 * r) + (0.71 * g) + (0.07 * b));
}
}
out.put(0, 0, data);
return out;
}
Ссылка: здесь
Чтобы преобразовать из BufferedImage в Mat, я использую следующий метод:
public static Mat img2Mat(BufferedImage image) {
image = convertTo3ByteBGRType(image);
byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3);
mat.put(0, 0, data);
return mat;
}
Перед преобразованием в Mat я меняю тип bufferedImage на TYPE_3BYTE_BGR, потому что для некоторых типов BufferedImages метод ((DataBufferByte) image.getRaster(). GetDataBuffer()). GetData(); может вернуть int[], и это нарушит код.
Ниже приведен метод преобразования в TYPE_3BYTE_BGR.
private static BufferedImage convertTo3ByteBGRType(BufferedImage image) {
BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(),
BufferedImage.TYPE_3BYTE_BGR);
convertedImage.getGraphics().drawImage(image, 0, 0, null);
return convertedImage;
}
Я нашел решение здесь. Решение аналогично Andriys.
Camera c;
c.Connect();
c.StartCapture();
Image f2Img, cf2Img;
c.RetrieveBuffer(&f2Img);
f2Img.Convert( FlyCapture2::PIXEL_FORMAT_BGR, &cf2Img );
unsigned int rowBytes = (double)cf2Img.GetReceivedDataSize()/(double)cf2Img.GetRows();
cv::Mat opencvImg = cv::Mat( cf2Img.GetRows(), cf2Img.GetCols(), CV_8UC3, cf2Img.GetData(),rowBytes );
Когда вы используете в качестве оболочки JavaCP библиотеку bytedeco (версия 1.5.3), вы можете использовать Java2DFrameUtils.
Простое использование:
import org.bytedeco.javacv.Java2DFrameUtils;
...
BufferedImage img = ImageIO.read(new File("some/image.jpg");
Mat mat = Java2DFrameUtils.toMat(img);
Примечание: не смешивайте разные обертки, bytedeco Mat отличается от opencv Mat.
Одним простым способом было бы создать новое использование
Mat newMat = Mat(rows, cols, type);
затем получите значения пикселей из вашего BufferedImage и поместите в newMat, используя
newMat.put(row, col, pixel);
Вы можете сделать это в OpenCV следующим образом:
File f4 = new File("aa.png");
Mat mat = Highgui.imread(f4.getAbsolutePath());