I'm using zxing to read barcodes from scanned images like this:
Ideally the barcode is always placed at 2/5 position but sometime the barcode is blurred, dirt or scratched, for testing purpose is required to save the bitmap sent to reader, based on this answer: Convert byte array of data type TYPE_4BYTE_ABGR to BufferedImage I'm trying to save the croppedBitmap without success, any help really appreciated.
private static String decodeFile(File file) throws IOException, NotFoundException {
BufferedImage bufferedImage = ImageIO.read(file);
LuminanceSource source = new
BufferedImageLuminanceSource(bufferedImage);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
System.out.println(bitmap.getWidth() + " x " + bitmap.getHeight());
int width = bitmap.getWidth();
int height = bitmap.getHeight() / 5;
int top = height;
BinaryBitmap croppedBitmap = bitmap.crop(0, top, width, height);
int[] dst = new int[width * height];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int a = croppedBitmap.getBlackMatrix().get(i, j) ? 1 & 0xff : 0;
int b = croppedBitmap.getBlackMatrix().get(i, j) ? 1 & 0xff : 0;
int g = croppedBitmap.getBlackMatrix().get(i, j) ? 1 & 0xff : 0;
int r = croppedBitmap.getBlackMatrix().get(i, j) ? 1 & 0xff : 0;
dst[(i + 1) * j] = (a << 24) | (r << 16) | (g << 8) | b;
}
}
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
image.setRGB(0, 0, width, height, dst, 0, width);
boolean r = ImageIO.write(image, "bmp", new File("croppedBitmap.bmp"));
System.out.println("Write bmp:" + r);
Hashtable<DecodeHintType, Object> hint = new Hashtable<DecodeHintType, Object>();
hint.put(DecodeHintType.TRY_HARDER, BarcodeFormat.CODE_39);
try {
MultiFormatReader reader = new MultiFormatReader();
Result result = reader.decode(bitmap, hint);
return result.getText();
} catch (NotFoundException e) {
System.out.println("Decode failed.");
return null;
}
}
Use toBufferedImage:
BitMatrix blackMatrix = croppedBitmap.getBlackMatrix();
BufferedImage image = MatrixToImageWriter.toBufferedImage(blackMatrix);
Related
At this time I tried the following solution. In this way the image is mirrored and the operations are computationally too onerous. I need to perform operations in about 40 ms.
int[] shifted = new int[CAPTURE_WIDTH * CAPTURE_HEIGHT];
// (byte) bgra to rgb (int)
for (int i = 0, j = 0; i < pbFrame.length; i = i + 4, j++) {
int b, g, r;
b = pbFrame[i] & 0xFF;
g = pbFrame[i + 1] & 0xFF;
r = pbFrame[i + 2] & 0xFF;
shifted[j] = (r << 16) | (g << 8) | b;
}
BufferedImage bufferedImage = new BufferedImage(CAPTURE_WIDTH, CAPTURE_HEIGHT, BufferedImage.TYPE_INT_RGB);
bufferedImage.getRaster().setDataElements(0, 0, CAPTURE_WIDTH, CAPTURE_HEIGHT, pbFrame);
String path = "C:\\Users\\Administrator\\Desktop\\images" +
String.format("%03d", n) + ".jpg";
File outputfile = new File(path); //create new outputfile object
ImageIO.write(bufferedImage, "JPG", outputfile);
n++
In C# the following code works:
Bitmap bitmap = new Bitmap(cx, cy, cx*3, PixelFormat.Format24bppRgb, pbFrame);
ImageCodecInfo myImageCodecInfo = GetEncoderInfo("image/jpeg");
bitmap.Save("./test.jpeg", myImageCodecInfo, null);
ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j
ImageCodecInfo[] encoders
encoders = ImageCodecInfo.GetImageEncoders();
for (j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}
I've used the ImageUtil class provided in https://stackoverflow.com/a/40152147/2949966 within my git repo: https://github.com/ahasbini/cameraview/tree/camera_preview_imp (note the implementation is in camera_preview_imp branch) to implement a frame preview callback. An ImageReader is set to preview frames in the ImageFormat.YUV_420_888 format which will be converted into ImageFormat.JPEG using the ImageUtil class and send it to the frame callback. The demo app saves a frame from the callback to a file every 50 frames. All of the saved frame images are coming out distorted similar to below:
If I've changed the ImageReader to use ImageFormat.JPEG instead by doing the following changes in Camera2:
mPreviewImageReader = ImageReader.newInstance(previewSize.getWidth(),
previewSize.getHeight(), ImageFormat.JPEG, /* maxImages */ 2);
mCamera.createCaptureSession(Arrays.asList(surface, mPreviewImageReader.getSurface()),
mSessionCallback, null);
the image is coming properly without any distortions however the frame rate drops significantly and the view starts to lag. Hence I believe the ImageUtil class is not converting properly.
Solution provided by #volodymyr-kulyk does not take into consideration the row stride of the planes within the image. Below code does the trick (image is of android.media.Image type):
data = NV21toJPEG(YUV420toNV21(image), image.getWidth(), image.getHeight(), 100);
And the implementations:
private static byte[] NV21toJPEG(byte[] nv21, int width, int height, int quality) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
yuv.compressToJpeg(new Rect(0, 0, width, height), quality, out);
return out.toByteArray();
}
private static byte[] YUV420toNV21(Image image) {
Rect crop = image.getCropRect();
int format = image.getFormat();
int width = crop.width();
int height = crop.height();
Image.Plane[] planes = image.getPlanes();
byte[] data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8];
byte[] rowData = new byte[planes[0].getRowStride()];
int channelOffset = 0;
int outputStride = 1;
for (int i = 0; i < planes.length; i++) {
switch (i) {
case 0:
channelOffset = 0;
outputStride = 1;
break;
case 1:
channelOffset = width * height + 1;
outputStride = 2;
break;
case 2:
channelOffset = width * height;
outputStride = 2;
break;
}
ByteBuffer buffer = planes[i].getBuffer();
int rowStride = planes[i].getRowStride();
int pixelStride = planes[i].getPixelStride();
int shift = (i == 0) ? 0 : 1;
int w = width >> shift;
int h = height >> shift;
buffer.position(rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift));
for (int row = 0; row < h; row++) {
int length;
if (pixelStride == 1 && outputStride == 1) {
length = w;
buffer.get(data, channelOffset, length);
channelOffset += length;
} else {
length = (w - 1) * pixelStride + 1;
buffer.get(rowData, 0, length);
for (int col = 0; col < w; col++) {
data[channelOffset] = rowData[col * pixelStride];
channelOffset += outputStride;
}
}
if (row < h - 1) {
buffer.position(buffer.position() + rowStride - length);
}
}
}
return data;
}
Method was gotten from the following link.
Updated ImageUtil:
public final class ImageUtil {
public static byte[] NV21toJPEG(byte[] nv21, int width, int height, int quality) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
yuv.compressToJpeg(new Rect(0, 0, width, height), quality, out);
return out.toByteArray();
}
// nv12: true = NV12, false = NV21
public static byte[] YUV_420_888toNV(ByteBuffer yBuffer, ByteBuffer uBuffer, ByteBuffer vBuffer, boolean nv12) {
byte[] nv;
int ySize = yBuffer.remaining();
int uSize = uBuffer.remaining();
int vSize = vBuffer.remaining();
nv = new byte[ySize + uSize + vSize];
yBuffer.get(nv, 0, ySize);
if (nv12) {//U and V are swapped
vBuffer.get(nv, ySize, vSize);
uBuffer.get(nv, ySize + vSize, uSize);
} else {
uBuffer.get(nv, ySize , uSize);
vBuffer.get(nv, ySize + uSize, vSize);
}
return nv;
}
public static byte[] YUV_420_888toI420SemiPlanar(ByteBuffer yBuffer, ByteBuffer uBuffer, ByteBuffer vBuffer,
int width, int height, boolean deInterleaveUV) {
byte[] data = YUV_420_888toNV(yBuffer, uBuffer, vBuffer, deInterleaveUV);
int size = width * height;
if (deInterleaveUV) {
byte[] buffer = new byte[3 * width * height / 2];
// De-interleave U and V
for (int i = 0; i < size / 4; i += 1) {
buffer[i] = data[size + 2 * i + 1];
buffer[size / 4 + i] = data[size + 2 * i];
}
System.arraycopy(buffer, 0, data, size, size / 2);
} else {
for (int i = size; i < data.length; i += 2) {
byte b1 = data[i];
data[i] = data[i + 1];
data[i + 1] = b1;
}
}
return data;
}
}
Operations to write in file byte[] data as JPEG:
//image.getPlanes()[0].getBuffer(), image.getPlanes()[1].getBuffer()
//image.getPlanes()[2].getBuffer(), image.getWidth(), image.getHeight()
byte[] nv21 = ImageUtil.YUV_420_888toI420SemiPlanar(yBuffer, uBuffer, vBuffer, width, height, false);
byte[] data = ImageUtil.NV21toJPEG(nv21, width, height, 100);
//now write `data` to file
!!! do not forget to close image after processing !!!
image.close();
Camera2 YUV_420_888 to Jpeg in Java(Android):
#Override
public void onImageAvailable(ImageReader reader){
Image image = null;
try {
image = reader.acquireLatestImage();
if (image != null) {
byte[] nv21;
ByteBuffer yBuffer = mImage.getPlanes()[0].getBuffer();
ByteBuffer uBuffer = mImage.getPlanes()[1].getBuffer();
ByteBuffer vBuffer = mImage.getPlanes()[2].getBuffer();
int ySize = yBuffer.remaining();
int uSize = uBuffer.remaining();
int vSize = vBuffer.remaining();
nv21 = new byte[ySize + uSize + vSize];
//U and V are swapped
yBuffer.get(nv21, 0, ySize);
vBuffer.get(nv21, ySize, vSize);
uBuffer.get(nv21, ySize + vSize, uSize);
String savingFilepath = getYUV2jpg(nv21);
}
} catch (Exception e) {
Log.w(TAG, e.getMessage());
}finally{
image.close();// don't forget to close
}
}
public String getYUV2jpg(byte[] data) {
File imageFile = new File("your parent directory", "picture.jpeg");//no i18n
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(imageFile));
bos.write(data);
bos.flush();
bos.close();
} catch (IOException e) {
return e.getMessage();
} finally {
try {
if (bos != null) {
bos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return imageFile.getAbsolutePath();
}
Note: Handle the image rotation issue.
I think there is some confusion here with NV and YV formats of YUV. NV (semi-planar) have interleaved U/V. YV (planar) do not. So the conversions being done here are YV12/21 not NV12/21.
I'm trying to convert an image from YUV to RGB inside onImageAvailable method in java.
I'm using openCV for conversion.
I can't use RGB format from android Camera2 for avoiding frame loss.
I can't chose the best format for conversion.
Image.Plane Y = image.getPlanes()[0];
Image.Plane U = image.getPlanes()[1];
Image.Plane V = image.getPlanes()[2];
Y.getBuffer().position(0);
U.getBuffer().position(0);
V.getBuffer().position(0);
int Yb = Y.getBuffer().remaining();
int Ub = U.getBuffer().remaining();
int Vb = V.getBuffer().remaining();
ByteBuffer buffer = ByteBuffer.allocateDirect( Yb + Ub + Vb);
buffer.put(Y.getBuffer());
buffer.put(U.getBuffer());
buffer.put(V.getBuffer());
// Image is 640 x 480
Mat yuvMat = new Mat(960, 640, CvType.CV_8UC1);
yuvMat.put(0, 0, buffer.array());
// I don't know what is the correct format
Mat rgbMat = new Mat(yuvMat.rows, yuvMat.cols, CvType.CV_8UC4);
Imgproc.cvtColor(yuvMat, rgbMat, Imgproc.COLOR_YUV420sp2RGBA);
final Bitmap bit = Bitmap.createBitmap(rgbMat.cols(), rgbMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(rgbMat, bit);
Actually, I obtain only cropped grayscale image
Try this function:
void decodeYUV420SP( byte[] rgb, byte[] yuv420sp, int width, int height )
{
final int frameSize = width * height;
for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0)
y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);
if (r < 0) r = 0; else if (r > 262143)
r = 262143;
if (g < 0) g = 0; else if (g > 262143)
g = 262143;
if (b < 0) b = 0; else if (b > 262143)
b = 262143;
//rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
int nIdx = ((width - i - 1) * height + height - j - 1) * 3;//device
//int nIdx = (i * height + j) * 3;//nox
rgb[nIdx] = (byte) (((r << 6) & 0xff0000)>>16);
rgb[nIdx+1] = (byte) (((g >> 2) & 0xff00)>>8);
rgb[nIdx+2] = (byte) ((b >> 10) & 0xff);
}
}
}
Use : decodeYUV420SP( rgb, camData, nWidth234, nHeight234 );
You can get RGB byte array;
If you need get the image from byte array, try this.
public boolean convertYunToJpeg(byte[] data, int width, int height){
YuvImage image = new YuvImage(data, ImageFormat.NV21, width, height, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int quailty = 20;
image.compressToJpeg(new Rect(0,0, width, height), quailty, baos);
byte[] jpegByteArray = baos.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(jpegByteArray, 0, jpegByteArray.length);
Matrix matrix = new Matrix();
matrix.postRotate(-90);
Bitmap lastbitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
try {
File file = new File(BaseApplication.DIRECTORY + mCode + ".png");
if(!file.exists()){
RandomAccessFile me = new RandomAccessFile(BaseApplication.DIRECTORY + mCode + ".png", "rw");
me.writeInt(5);
me.close();
file = new File(BaseApplication.DIRECTORY + mCode + ".png");
}
FileOutputStream fos = new FileOutputStream(file);
lastbitmap.compress(Bitmap.CompressFormat.PNG, quailty, fos);
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
I knwo there is already an question like this. But its solution was not suitable for me because with the Sehellfolder Methode you can only get 16x16 and 32x32 sized icons.
I have extracted a HICO with size of 256x256 and want to convert it into and Java Image like BufferedImage. I found and method for it to. But it does not work properly:
public static BufferedImage getIcon(final WinDef.HICON hIcon,int ICON_SIZE,short ICON_DEPTH,int ICON_BYTE_SIZE) {
final int width = ICON_SIZE;
final int height = ICON_SIZE;
final short depth = ICON_DEPTH;
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
final Memory lpBitsColor = new Memory(width * height * depth / ICON_BYTE_SIZE);
final Memory lpBitsMask = new Memory(width * height * depth / ICON_BYTE_SIZE);
final WinGDI.BITMAPINFO info = new WinGDI.BITMAPINFO();
final WinGDI.BITMAPINFOHEADER hdr = new WinGDI.BITMAPINFOHEADER();
info.bmiHeader = hdr;
hdr.biWidth = width;
hdr.biHeight = height;
hdr.biPlanes = 1;
hdr.biBitCount = depth;
hdr.biCompression = WinGDI.BI_RGB;
final WinDef.HDC hDC = User32.INSTANCE.GetDC(null);
final WinGDI.ICONINFO piconinfo = new WinGDI.ICONINFO();
User32.INSTANCE.GetIconInfo(hIcon, piconinfo);
GDI32.INSTANCE.GetDIBits(hDC, piconinfo.hbmColor, 0, height, lpBitsColor, info, WinGDI.DIB_RGB_COLORS);
GDI32.INSTANCE.GetDIBits(hDC, piconinfo.hbmMask, 0, height, lpBitsMask, info, WinGDI.DIB_RGB_COLORS);
int r, g, b, a, argb;
int x = 0, y = height - 1;
for (int i = 0; i < lpBitsColor.size(); i = i + 3) {
b = lpBitsColor.getByte(i) & 0xFF;
g = lpBitsColor.getByte(i + 1) & 0xFF;
r = lpBitsColor.getByte(i + 2) & 0xFF;
a = 0xFF - lpBitsMask.getByte(i) & 0xFF;
argb = a << 24 | r << 16 | g << 8 | b;
image.setRGB(x, y, argb);
x = (x + 1) % width;
if (x == 0) {
y--;
}
}
User32.INSTANCE.ReleaseDC(null, hDC);
GDI32.INSTANCE.DeleteObject(piconinfo.hbmColor);
GDI32.INSTANCE.DeleteObject(piconinfo.hbmMask);
return image;
}
Resulting Image
Do you know andy method that works better?
EDIT:
public static BufferedImage getImageByHICON(final int width, final int height, final WinNT.HANDLE hicon, final WinGDI.BITMAPINFOHEADER info) {
final WinGDI.ICONINFO iconinfo = new WinGDI.ICONINFO();
try {
// GDI32 g32 = GDI32.INSTANCE;
// get icon information
if (!User32.INSTANCE.GetIconInfo(new WinDef.HICON(hicon.getPointer()), iconinfo)) { return null; }
final WinDef.HWND hwdn = new WinDef.HWND();
final WinDef.HDC dc = User32.INSTANCE.GetDC(hwdn);
if (dc == null) {
return null; }
try {
final int nBits = width * height * 4;
// final BitmapInfo bmi = new BitmapInfo(1);
final Memory colorBitsMem = new Memory(nBits);
// // Extract the color bitmap
final WinGDI.BITMAPINFO bmi = new WinGDI.BITMAPINFO();
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = WinGDI.BI_RGB;
GDI32.INSTANCE.GetDIBits(dc, iconinfo.hbmColor, 0, height, colorBitsMem, bmi, WinGDI.DIB_RGB_COLORS);
// g32.GetDIBits(dc, iconinfo.hbmColor, 0, size, colorBitsMem,
// bmi,
// GDI32.DIB_RGB_COLORS);
final int[] colorBits = colorBitsMem.getIntArray(0, width * height);
if (info.biBitCount < 32) {
final Memory maskBitsMem = new Memory(nBits);
// // Extract the mask bitmap
GDI32.INSTANCE.GetDIBits(dc, iconinfo.hbmMask, 0, height, maskBitsMem, bmi, WinGDI.DIB_PAL_COLORS);
// g32.GetDIBits(dc, iconinfo.hbmMask, 0, size,
// maskBitsMem,
// bmi,
// // GDI32.DIB_RGB_COLORS);
final int[] maskBits = maskBitsMem.getIntArray(0, width * height);
// // // Copy the mask alphas into the color bits
for (int i = 0; i < colorBits.length; i++) {
colorBits[i] = colorBits[i] | (maskBits[i] != 0 ? 0 : 0xFF000000);
}
}
final BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
bi.setRGB(0, 0, width, height, colorBits, 0, height);
return bi;
} finally {
com.sun.jna.platform.win32.User32.INSTANCE.ReleaseDC(hwdn, dc);
}
} finally {
User32.INSTANCE.DestroyIcon(new WinDef.HICON(hicon.getPointer()));
GDI32.INSTANCE.DeleteObject(iconinfo.hbmColor);
GDI32.INSTANCE.DeleteObject(iconinfo.hbmMask);
}
}
Better Image
You need to use the method from Example 3 from this website
I have array of byte for image , and I want to rotate image by this array ,
this is my code :
BufferedImage img = ImageUtil.load(inputImagePath);
WritableRaster raster = img .getRaster();
DataBufferByte data = (DataBufferByte) raster.getDataBuffer();
byte [] pixel = data.getData();
how i can do this ? ,
thanks
After some work, I came up with this:
public class ImageRotation {
public static void main(String[] args) throws IOException {
BufferedImage img = ImageIO.read(
ImageRotation.class
.getResourceAsStream("Capture.PNG"));
JPanel pane = new JPanel();
pane.setLayout(new BorderLayout());
pane.add(
new JLabel("Original", new ImageIcon(img), JLabel.CENTER),
BorderLayout.WEST);
pane.add(
new JLabel("Rotated", new ImageIcon(rotateClockwise(img)), JLabel.CENTER),
BorderLayout.EAST);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(pane);
frame.setVisible(true);
frame.pack();
}
static BufferedImage rotateClockwise(BufferedImage img) {
int[] origPix = getIntBuff(img);
int newWidth = img.getHeight();
int newHeight = img.getWidth();
int[] buff = new int[newWidth * newHeight];
// formula for determining pixel mapping
// (sizeOf(old y) - 1) - old y -> new x
// old x -> new y
for (int x = 0; x < img.getWidth(); x++)
for (int y = 0; y < img.getHeight(); y++) {
int pix = origPix[x + (y * img.getWidth())];
int newX = img.getHeight() - 1 - y, newY = x;
buff[newX + (newWidth * newY)] = pix;
}
// we have now rotated the array clockwise, time to place the buffer in an image
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = new BufferedImage(newWidth, newHeight, type);
WritableRaster wr = ret.getRaster();
wr.setDataElements(0, 0, newWidth, newHeight, buff);
return ret;
}
// variation of convertTo2DWithoutUsingGetRGB http://stackoverflow.com/a/9470843/4683264
private static int[] getIntBuff(BufferedImage image) {
final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
final int width = image.getWidth();
final int height = image.getHeight();
final boolean hasAlphaChannel = image.getAlphaRaster() != null;
int[] result = new int[height * width];
final int pixelLength = hasAlphaChannel ? 4 : 3;
for (int pixel = 0, resInd = 0; pixel < pixels.length; pixel += pixelLength) {
int argb = 0;
if (hasAlphaChannel)
argb += (((int) pixels[pixel] & 0xff) << 24); // alpha
else
argb += -16777216; // 255 alpha
argb += ((int) pixels[pixel + 1] & 0xff); // blue
argb += (((int) pixels[pixel + 2] & 0xff) << 8); // green
argb += (((int) pixels[pixel + 3] & 0xff) << 16); // red
result[resInd++] = argb;
}
return result;
}
}
Result:
Right now it only rotates the image clockwise, but once you find the pixel mappings from the old to new image for, say, counterclockwise, all you need to change is in the nested for loop in the rotateClockwise method to:
int newX = y, newY = img.getWidth() - 1 - x;