I am developing application for recognising Sudoku from paper and showing it on the screen. Since I am using OpenCV, I've tried to convert Mat object to PNG image. I tried to use this code:
private static void Mat2BufferedImage(Mat m, String s){
int type = BufferedImage.TYPE_BYTE_GRAY;
if ( m.channels() > 1 ) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
int bufferSize = m.channels()*m.cols()*m.rows();
byte [] b = new byte[bufferSize];
m.get(0,0,b);
BufferedImage image = new BufferedImage(m.cols(),m.rows(), type);
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(b, 0, targetPixels, 0, b.length);
File f = new File("resources/img-" + s + ".png");
try {
ImageIO.write(image, "PNG", f);
} catch (IOException e) {
e.printStackTrace();
}
}
, but I am unable since BufferedImage is not supported anymore. Is there any other way to do this? I am not so good with OpenCV.
Related
I want to compress jpg file if it bigger than 1MB to 1MB or a bit smaller.
Param compressionQuality from 0.0f to 1.0f in setCompressionQuality in ImageWriteParam is not clearly defined.
Quality = 0.5f can compress the image 5 times, 7 times, 1.2 times depends on the image. Not 2 times as everybody expected.
So, how can I choose the size of compressing image? Maybe there are any open source libraries?
UPD:
Example of compressing:
public static byte[] write(BufferedImage image, String formatName, int dpi, float quality) {
try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
ImageIOUtil.writeImage(image, formatName, outputStream, dpi, quality);
return outputStream.toByteArray();
} catch (IOException ex) {
return null;
}
}
public static void main(String[] args) {
BufferedImage bufImg = null;
try
{
bufImg = ImageIO.read(new File("myfile"));
} catch (IOException e){e.printStackTrace();}
for(int i = 0; i < 10; i++) {
byte[] arr = write(bufImg, "jpg", 300, 0.1f*i);
double fl = (double)arr.length/(double)MY_FILE_SIZE_BYTES;
System.out.println(fl + " " + (0.1f*i));
}
}
I use org.apache.pdfbox.tools.imageio.ImageIOUtil for that, ImageIOUtil uses ImageWriter and ImageWriteParam inside.
So I tried looking for the solution but could not find a solution where I can convert the RGBA to RGB format.
If a simple solution from BufferedImage to BufferedImage conversion is given then that will be best, otherwise the problem is as follows :
Basically I have to convert BufferedImage into MAT format. It works properly for JPG/JPEG images but not PNGs. Following code I use for the conversion ::
BufferedImage biImg = ImageIO.read(new File(imgSource));
mat = new Mat(biImg.getHeight(), biImg.getWidth(),CvType.CV_8UC3);
Imgproc.cvtColor(mat,matBGR, Imgproc.COLOR_RGBA2BGR);
byte[] data = ((DataBufferByte) biImg.getRaster().getDataBuffer()).getData();
matBGR.put(0, 0, data);
This throws error for images with RGBA values. So thus looking for a solution.
Thanks in advance.
I found a solution like this :
BufferedImage oldRGBA= null;
try {
oldRGBA= ImageIO.read(new URL("http://yusufcakmak.com/wp-content/uploads/2015/01/java_ee.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
final int width = 1200;
final int height = 800;
BufferedImage newRGB = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
newRGB .createGraphics().drawImage(oldRGBA, 0, 0, width, height, null);
try {
ImageIO.write(newRGB , "PNG", new File("your path"));
} catch (IOException e) {}
So here when we creating new BufferedImage we can change type of the image with :
The RGB worked for me with PNG.
public static BufferedImage toBufferedImageOfType(BufferedImage original, int type) {
if (original == null) {
throw new IllegalArgumentException("original == null");
}
if (original.getType() == type) {
return original;
}
BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), type);
Graphics2D g = image.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.drawImage(original, 0, 0, null);
}
finally {
g.dispose();
}
return image;
}
I'm getting preview frames using OnImageAvailableListener:
#Override
public void onImageAvailable(ImageReader reader) {
Image image = null;
try {
image = reader.acquireLatestImage();
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
byte[] data = new byte[buffer.capacity()];
buffer.get(data);
//data.length=332803; width=3264; height=2448
Log.e(TAG, "data.length=" + data.length + "; width=" + image.getWidth() + "; height=" + image.getHeight());
//TODO data processing
} catch (Exception e) {
e.printStackTrace();
}
if (image != null) {
image.close();
}
}
Each time length of data is different but image width and height are the same.
Main problem: data.length is too small for such resolution as 3264x2448.
Size of data array should be 3264*2448=7,990,272, not 300,000 - 600,000.
What is wrong?
imageReader = ImageReader.newInstance(3264, 2448, ImageFormat.JPEG, 5);
I solved this problem by using YUV_420_888 image format and converting it to JPEG image format manually.
imageReader = ImageReader.newInstance(MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT,
ImageFormat.YUV_420_888, 5);
imageReader.setOnImageAvailableListener(this, null);
Surface imageSurface = imageReader.getSurface();
List<Surface> surfaceList = new ArrayList<>();
//...add other surfaces
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(imageSurface);
surfaceList.add(imageSurface);
cameraDevice.createCaptureSession(surfaceList,
new CameraCaptureSession.StateCallback() {
//...implement onConfigured, onConfigureFailed for StateCallback
}, null);
#Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image != null) {
//converting to JPEG
byte[] jpegData = ImageUtils.imageToByteArray(image);
//write to file (for example ..some_path/frame.jpg)
FileManager.writeFrame(FILE_NAME, jpegData);
image.close();
}
}
public final class ImageUtil {
public static byte[] imageToByteArray(Image image) {
byte[] data = null;
if (image.getFormat() == ImageFormat.JPEG) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
data = new byte[buffer.capacity()];
buffer.get(data);
return data;
} else if (image.getFormat() == ImageFormat.YUV_420_888) {
data = NV21toJPEG(
YUV_420_888toNV21(image),
image.getWidth(), image.getHeight());
}
return data;
}
private static byte[] YUV_420_888toNV21(Image image) {
byte[] nv21;
ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
ByteBuffer vuBuffer = image.getPlanes()[2].getBuffer();
int ySize = yBuffer.remaining();
int vuSize = vuBuffer.remaining();
nv21 = new byte[ySize + vuSize];
yBuffer.get(nv21, 0, ySize);
vuBuffer.get(nv21, ySize, vuSize);
return nv21;
}
private static byte[] NV21toJPEG(byte[] nv21, int width, int height) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out);
return out.toByteArray();
}
}
public final class FileManager {
public static void writeFrame(String fileName, byte[] data) {
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileName));
bos.write(data);
bos.flush();
bos.close();
// Log.e(TAG, "" + data.length + " bytes have been written to " + filesDir + fileName + ".jpg");
} catch (IOException e) {
e.printStackTrace();
}
}
}
I am not sure, but I think you are taking only one of the plane of the YUV_420_888 format (luminance part).
In my case, I usually transform my image to byte[] in this way.
Image m_img;
Log.v(LOG_TAG,"Format -> "+m_img.getFormat());
Image.Plane Y = m_img.getPlanes()[0];
Image.Plane U = m_img.getPlanes()[1];
Image.Plane V = m_img.getPlanes()[2];
int Yb = Y.getBuffer().remaining();
int Ub = U.getBuffer().remaining();
int Vb = V.getBuffer().remaining();
data = new byte[Yb + Ub + Vb];
//your data length should be this byte array length.
Y.getBuffer().get(data, 0, Yb);
U.getBuffer().get(data, Yb, Ub);
V.getBuffer().get(data, Yb+ Ub, Vb);
final int width = m_img.getWidth();
final int height = m_img.getHeight();
And I use this byte buffer to transform to rgb.
Hope this helps.
Cheers.
Unai.
Your code is requesting JPEG-format images, which are compressed. They'll change in size for every frame, and they'll be much smaller than the uncompressed image. If you want to do nothing besides save JPEG images, you can just save what you have in the byte[] data to disk and you're done.
If you want to actually do something with the JPEG, you can use BitmapFactory.decodeByteArray() to convert it to a Bitmap, for example, though that's pretty inefficient.
Or you can switch to YUV, which is more efficient, but you need to do more work to get a Bitmap out of it.
I have this code where i get an InputStream and create an image:
Part file;
// more code
try {
InputStream is = file.getInputStream();
File f = new File("C:\\ImagenesAlmacen\\QR\\olaKeAse.jpg");
OutputStream os = new FileOutputStream(f);
byte[] buf = new byte[1024];
int len;
while ((len = is.read(buf)) > 0) {
os.write(buf, 0, len);
}
os.close();
is.close();
} catch (IOException e) {
System.out.println("Error");
}
The problem is that I have to resize that image before i create if from the InputStream
So how to resize what I get from the InputStream and then create that resized image. I want to set the largest side of the image to 180px and resize the other side with that proportion.
Example:
Image = 289px * 206px
Resized image = 180px* 128px
I did this:
try {
InputStream is = file.getInputStream();
Image image = ImageIO.read(is);
BufferedImage bi = this.createResizedCopy(image, 180, 180, true);
ImageIO.write(bi, "jpg", new File("C:\\ImagenesAlmacen\\QR\\olaKeAse.jpg"));
} catch (IOException e) {
System.out.println("Error");
}
BufferedImage createResizedCopy(Image originalImage, int scaledWidth, int scaledHeight, boolean preserveAlpha) {
int imageType = preserveAlpha ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage scaledBI = new BufferedImage(scaledWidth, scaledHeight, imageType);
Graphics2D g = scaledBI.createGraphics();
if (preserveAlpha) {
g.setComposite(AlphaComposite.Src);
}
g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
g.dispose();
return scaledBI;
}
And I did not use the other code.
Hope helps someone!
I'm trying to develop an application in Android and I'm having a problem I can't figure out how to solve.
Description:
The application consists of image processing and one of the routines is as follows. An image file (PNG) is converted into a array of bytes databyteimage[] with n elements, a part of this array ex: from databyteimage[i] to databyteimage[i+k] consecutive with k elements and " i " is offset databyteimage[], the LSB (Least Significant Bit) is replaced, the value what is replaced coms from other array of bytes ex:datareplace[] with m elements the value of k is m*8. This operation is done using operations on bits . After this process, a new string databyteimage[] is created.
The problem:
When trying to create the BITMAP object from the new array databyteimage[] returns NULL to displaty or show the new image.
I would appreciate if you could help me find a solution to this problem, since until now no one could help me.
***// GetByte method from Image***
private byte[] getByteImageData(String filePath) {
/*
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
Bitmap mutable = bitmap.copy(Bitmap.Config.RGB_565, true);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mutable.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
*/
byte[] _imagebytedata = new byte[1024];
InputStream _input = null;
try {
if (filePath != null && (filePath.length() > 0)) {
// Create a file for image
File _fileimage = new File(filePath);
if (_fileimage.exists()) {
// Get the byte from file image
_input = new BufferedInputStream(new FileInputStream(
_fileimage));
_imagebytedata = new byte[(int) _fileimage.length()];
_input.read(_imagebytedata, 0, (int) _fileimage.length());
_input.close();
}
}
} catch (Exception e) {
}
**// Bitwise operations to change LSB of byte array image**
private byte[] Text(byte[] imagedata, byte[] textmess, int offset) {
for (int i = 0; i < textmess.length; ++i) {
int add = textmess[i];
for (int bit = 7; bit >= 0; --bit, ++offset) {
int b = (add >>> bit) & 1;
imagedata[offset] = (byte) ((imagedata[offset] & 0xFE) |b);
}
}
return imagedata;
}
***//Save image from new byte array***
private boolean saveImage(String pathFile,byte[] encodedimage) {
OutputStream _output = null;
File _newFileImage = new File(pathFile);
byte[] _encodedimage = encodedimage;
//Bitmap _imagebitmap = BitmapFactory.decodeByteArray(encodedimage, 0, encodedimage.length);
if (_newFileImage.exists()) {
try {
_output = new BufferedOutputStream(new FileOutputStream(
_newFileImage));
_output.write(_encodedimage, 0, _encodedimage.length);
_output.flush();
_output.close();
return true;
} catch (Exception e) {
}
;
}// _newFileImage.exists()
return false;
}
public boolean encodeTextInFile(String filepath, String text) {
byte[] _newimagebytedata;
byte[] _imagebytedata = getByteImageData(filepath);
byte[] _textbytedata = text.getBytes();
byte[] _lengthbytedata = byteConversion(text.length());
_newimagebytedata = Text(_imagebytedata, _lengthbytedata, 33);
_newimagebytedata = Text(_imagebytedata, _textbytedata, 65);
**// The value of variable _bitmapdoi is null here is the problem**
Bitmap _bitmapdoi = BitmapFactory.decodeByteArray(_newimagebytedata, 0,_newimagebytedata.length);
return saveImage(filepath, _newimagebytedata);
}
What you are reading in getByteImageData is not a bitmap. It is a file, most likely a compressed image. Working on the bytes from this file is very different from working on the image pixels. I suggest you work on the actual Bitmap object:
Load the bitmap:
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
// Not quite sure if the returned bitmap is mutable, so
Bitmap mutable = bitmap.copy(Bitmap.Config.RGB_565, true);
Modify a pixel:
int pixelRGB = mutable.getPixel(x, y);
// Do whatever you have to do
mutable.setPixel(x, y, pixelRGB);
Write it back:
mutable.compress(Bitmap.CompressFormat.PNG, 100, new ByteArrayOutputStream(new FileOutputStream(_newFileImage)));