Creating a YUVFormat instance - java

I would like to create a jmf/fmj YUVFormat instance for a dynamically created CaptureDevice using YUV420. I am confused as to what the values are supposed to be for strideY, strideUV, offsetY, offsetU, and offsetV. Only the following constructors are available in the YUVFormat class:
1. YUVFormat()
2. YUVFormat(int yuvType)
3. YUVFormat(java.awt.Dimension size, int maxDataLength, java.lang.Class dataType, float frameRate, int yuvType, int strideY, int strideUV, int offsetY, int offsetU, int offsetV)
Using #1 or #2 doesn't allow me to set size, frame rate, or data type after the fact; so I can't use them. Using #3 requires me to know the five additional parameters. I've read all the following posts from my google search, but I'm still confused as to what the values should be. I think that I can safely assume the strideY and strideUV will be the width of the frame, but I'm not 100% sure.
Javadoc: http://fmj-sf.net/doc/fmj/javax/media/format/YUVFormat.html
MediaWiki: http://wiki.multimedia.cx/index.php?title=PIX_FMT_YUV420P
FourCC: http://www.fourcc.org/yuv.php#IYUV
Here's my code so far:
int strideY = width, strideUV = width / 2;
int offsetY = 0, offsetU = 0, offsetV = 0;
YUVFormat yuv = new YUVFormat(new Dimension(width, height), Format.NOT_SPECIFIED, Format.byteArray, frameRate, YUVFormat.YUV_420, strideY, strideUV, offsetY, offsetU, offsetV);

Last time I used those classes I had memory issues from them internally.
The format should not really need data rate or frame rate. It merely specifies how pixels are arranged in memory.
I would advise to handle the bytes in arrays if possible.
Think of RGBA data. Each word in the memory is 4 pixels. [RGBA][RGBA]... And typically it writes out the bottom left first, and ends at the top right. The size of data is easy to know and specific pixels easy to manipulate.
YUV is a planar or semi planar format with 12 bits per pixel on average rather than 32 bits. This is achieved by having 8 bits Y and 8 bits U and V with the U and V double sized. The 8 bits of U and V cover 4 pixels of the Y plane.
So if the picture size is 320 by 240, the first 320 * 240 bytes will be the Y-plane data.
The next bytes in memory are either interlaced U/V lines as semi planar or all planar with first all U then all V data.
The stride of Y is the width.
The stride of U/V is half the width.
The offset of Y is the number of bytes between pixel rows/strides.
The offset of U is the number of bytes between pixel rows/strides.
The offset of V is the number of bytes between pixel rows/strides.
They also have 'base address' which is not exposed in java. The memory address of the first Y pixel data.
On systems that can only allocate 32 bit words of memory as a minimum, images using 12 bit color depth or odd pixel sizes can make the host system behave in different ways regarding where the pixel data resides in addressed memory.
for instance,
write all the Y data packed, it will have a zero offset.
Next write one horizontal line of U data.
Next write one horizontal line of V data.
Next write one horizontal line of U data.
Next write one horizontal line of V data.
The stride of U and V are half the stride of Y.
In java, you should be able to use zero offsets by writing pixel data without gaps between U and V data.
The other format of yuv writes all the U and then all the V data in full chunks.
The offset corresponds to the number of bytes between single Y/U/V rows.
Base address would correspond to the starting address of the U/V planes.
data starts 'here(base)' is this 'wide(stride)' with the next row starting there(offset)
With java the base address is likely given.
Probably didnt answer the question lol
{
unsigned int planeSize;
unsigned int halfWidth;
unsigned char * yplane;
unsigned char * uplane;
unsigned char * vplane;
const unsigned char * rgbIndex;
int x, y;
unsigned char * yline;
unsigned char * uline;
unsigned char * vline;
planeSize = srcFrameWidth * srcFrameHeight;
halfWidth = srcFrameWidth >> 1;
// get pointers to the data
yplane = yuv;
uplane = yuv + planeSize;
vplane = yuv + planeSize + (planeSize >> 2);
rgbIndex = rgb;
for (y = 0; y < srcFrameHeight; y++)
{
yline = yplane + (y * srcFrameWidth);
uline = uplane + ((y >> 1) * halfWidth);
vline = vplane + ((y >> 1) * halfWidth);
if (flip)
rgbIndex = rgb + (srcFrameWidth*(srcFrameHeight-1-y)*rgbIncrement);
for (x = 0; x < (int) srcFrameWidth; x+=2)
{
rgbtoyuv(rgbIndex[0], rgbIndex[1], rgbIndex[2], *yline, *uline, *vline);
rgbIndex += rgbIncrement;
yline++;
rgbtoyuv(rgbIndex[0], rgbIndex[1], rgbIndex[2], *yline, *uline, *vline);
rgbIndex += rgbIncrement;
yline++;
uline++;
vline++;
}
}
}
In java..
public static byte[] YV12toYUV420Planar(byte[] input, byte[] output, int width, int height) {
final int frameSize = width * height;
final int qFrameSize = frameSize/4;
System.arraycopy(input, 0, output, 0, frameSize); // Y
System.arraycopy(input, frameSize, output, frameSize + qFrameSize, qFrameSize); // Cr (V)
System.arraycopy(input, frameSize + qFrameSize, output, frameSize, qFrameSize); // Cb (U)
return output;
}

Strides and offsets are dependent on the Frame memory layout and the video frame dimensions and possible padding.
In general the stride (explained here) is the amount of bytes
you need to add to a pointer to go from one plane line to the next.
The offset is the amount of bytes you need to add to move from the start of the frame to a specific plane (Y, U or V)
See this Microsoft article explaining the various YUV Frame memory layouts.
Also see this Android source where the strides and offsets are calculated depending on the FOURCC (only for Android supported formats).

Related

Trying to get multiple images out of a single image

I've been stuck at something recently.
What I want to do is to get multiple sub-images out of 1 big image.
So take this example. I have a frame of 128x128 pixels where all the images need to be in.
I'm putting all the bufferedImages inside a list and scaling all those images to 128x128.
The image you see on that link is showing that I need 4 sub-images from that image, so at the end, I have 4 images which are 128x128 but 4 times.
Or if you have an image with 128x384 it will give 3 sub-images going from top to bottom.
https://i.stack.imgur.com/RsCkf.png
I know there is a function called
BufferedImage.getSubimage(int x, int y, int w, int h);
But the problem is that I can't figure out what math I need to implement.
What I tried is if the height or width is higher than 200 then divide it by 2 but that never worked for me.
I'm not sure I fully understand what you are asking, but I think what you want is something like this:
First, loop over the image in both dimensions.
Then compute the size of the tile (the smaller value of 128 and (image dimension - start pos)). This is to make sure you don't try to fetch a tile out of bounds. If your images are always a multiple of 128 in any dimension, you could just skip this step and just use 128 (just make sure you validate that input images follow this assumption).
If you only want tiles of exactly 128x128, you could also just skip the remainder, if the tile is less than 128x128, I'm not sure what your requirement is here. Anyway, I'll leave that to you. :-)
Finally, get the subimage of that size and coordinates and store in the list.
Code:
BufferedImage image = ...;
int tileSize = 128;
List<BufferedImage> tiles = new ArrayList<>();
for (int y = 0; y < image.height(); y += tileSize) {
int h = Math.min(tileSize, image.height() - y);
for (int x = 0; x < image.width(); x += tileSize) {
int w = Math.min(tileSize, image.width() - x);
tiles .add(image.getSubimage(x, y, w, h));
}
}

Convert yuv420 to jpeg in android camera2

I am trying to save yuv 420 preview frames obtained in android camera2 to jpeg. The only way I found of doing it was to convert the yuv420 to nv21, construct a yuvimage and then use the compresstojpeg method to get the jpeg. In order to convert from yuv420 to jpeg I am using the logic below
Image.Plane Y = img.getPlanes()[0];
Image.Plane U = img.getPlanes()[2];
Image.Plane V = img.getPlanes()[1];
int Yb = Y.getBuffer().remaining();
int Ub = U.getBuffer().remaining();
int Vb = V.getBuffer().remaining();
byte[] data = new byte[Yb + Ub + Vb];
Y.getBuffer().get(data, 0, Yb);
U.getBuffer().get(data, Yb, Ub);
V.getBuffer().get(data, Yb + Ub, Vb);
YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21,
mPreviewSize.getWidth(), mPreviewSize.getHeight(), null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
yuvImage.compressToJpeg(new Rect(0, 0,
mPreviewSize.getWidth(), mPreviewSize.getHeight()),
100, out);
However this results in getting green images for certain resolutions 144 x 176 , 176x144, 352x288, 480x360, 1280x960. Is the logic for converting to nv21 correct? What other way can I use for converting to jpeg from yuv420.
Is there any Java/Android api for this?
No, this isn't correct - you're not paying attention to the Plane's row stride or pixel stride.
You have to parse those, and make sure that your output buffer actually matches the input expectations of YuvImage's NV21 input, which assumes row stride = width, and interleaved V/U planes.
The code you have will only work if the input Image U/V planes are actually interleaved (in which case you're adding twice the UV data you need, but the first copy happens to be right layout...), and if width==row stride. Whether width==row stride depends on the resolution; usually the stride has to be a multiple of 16 pixels or something similar due to hardware restrictions. So for resolutions that aren't a multiple of 16, for example, your code wouldn't work.
Please fix both issues - paying attention to row and pixel stride; otherwise you might make it work on your device by accident, and still have it broken on devices with different parameters for strides.
Edit:
Some sample C++ code that does this kind of conversion can be found in the Android AOSP camera service code: CallbackProcessor::convertFromFlexibleYuv.
Mappings for reference:
previewFormat of HAL_PIXEL_FORMAT_YCrCb_420_SP is NV21.
src.data is plane 0
src.dataCb is is plane 1
src.dataCr is plane 2
src.stride is plane 0 rowStride
src.chromaStride is plane 1 and 2 rowStride
src.chromaStep is plane 1 and2 pixelStride
plane 0 pixel stride is 1

Multiplying Pixel Values in BufferedImage results in strange Behaviour

I am currently working on a program to help photographers with the creation of timelapses.
It calculates an decline or rise in brightness over a series of images. So the change in Exposure and iso for example dont affect the overall decline in brightness.
For this i use a simple Swing-based Interface which displays the first and last image. Under them are sliders to adjust the Brightness of the image.
This is applied via a direct manipulation of the BufferedImages underlying DataBuffer.
Mostly this works but i encountered some images which seem to have kind of a problem.
Do you have an idea why this is happening?
public BufferedImage getImage(float mult){
BufferedImage retim;
retim = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
Graphics g = retim.getGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
DataBufferByte db = (DataBufferByte) retim.getRaster().getDataBuffer();
byte[] bts = db.getData();
for(int i=0;i<bts.length;i++){
float n = bts[i]*mult;
if(n > 255){
bts[i]= (byte) 255;
}else{
bts[i] = (byte) n;
}
}
return retim;
}
This is the method which takes an float and multiplies every pixel in the image with it. (And some code to prevent the byte values from overflowing).
This is the unwanted behaviour (on the left) and the expected on the right.
Your problem is this line, and it occurs due to the fact that Java bytes are signed (in the range [-128...127]):
float n = bts[i] * mult;
After the multiplication, your n variable may be negative, thus causing the overflow to occur.
To fix it, use a bit mask to get the value as an unsigned integer (in the range [0...255]), before multiplying with the constant:
float n = (bts[i] & 0xff) * mult;
A better fix yet, is probably to use the RescaleOp, which is built to do brightness adjustments on BufferedImages.
Something like:
public BufferedImage getImage(float mult) {
return new RescaleOp(mult, 0, null).filter(img, null);
}
This is due to the capping of the value in certain bytes in the image.
For example (assuming RGB simple colour space):
The pixel starts at (125,255,0), if you multiply by factor 2.0, the result is (255,255,0). This is a different hue than the original.
This is also why the strange results only occur on pixels that already have high brightness to start with.
This link may help with better algorithm for adjusting brightness.
You could also refer to this related question.

Get convolution matrix from image samples?

Im trying to decompile the convolution matrix for the filters on the Motorola Gallery App
Im using the following code to read the pixel data:
public static void main(String[] foo) {
new JavaWalkBufferedImageTest1();
}
public void printPixelARGB(int pixel) {
int alpha = (pixel >> 24) & 0xff;
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel) & 0xff;
System.out.println("argb: " + alpha + ", " + red + ", " + green + ", " + blue);
}
private void marchThroughImage(BufferedImage image) {
int w = image.getWidth();
int h = image.getHeight();
System.out.println("width, height: " + w + ", " + h);
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
System.out.println("x,y: " + j + ", " + i);
int pixel = image.getRGB(j, i);
printPixelARGB(pixel);
System.out.println("");
}
}
}
public JavaWalkBufferedImageTest1() {
try {
// get the BufferedImage, using the ImageIO class
BufferedImage image =
ImageIO.read(this.getClass().getResource("WhiteSpot.jpg"));
marchThroughImage(image);
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
It gives the desired output.
But this is not leading me anywhere into finding the matrix.
How do I modify the code so that I can input 2 image files, Original & Filtered. And get the convolution matrix. Or is there an online tool that I can use, where I upload multiple Original & Filtered samples, and get the convolution matrix?
I would approach this problem (will work if only convolution was applied and only once!) like this:
first you need to find matrix size.
So you can loop through all possible matrix sizes or use a big one expecting zero values inside. That can be slow but you can try to estimate matrix size from the bluryness of sharp intensity edges (to how many pixels is the color bleeded).
for tested (big) matrix size try to find zero values
So for each tested matrix value form 2 arrays. One with few samples of pixels from convoluted image and second with pixels (shifted by tested matrix position) from original image. Now compute correlation coefficient between the two and if no significant correlation present you can assume the value in matrix is zero.
solve the remaining nonzero values in matrix
You can do this algebraically (form as many equation as many nonzero values you have (do not forget to choose pixels with different color). Or you can do search to minimize error/distance of convoluted output by your matrix and the real convoluted output. You can use something like mine approx class in C++ for this but if Your matrix is big then this will take a lot of time.
[Notes]
If more then just single convolution is applied to image then this will most likely not work.
Convolution matrices are mostly square and symmetric around mid value so you can compute the quarter of values and mirror the rest ... speeding up the computation.
Also see Dealing with filters and colour's which is similar question.
Bullet #2 can be used to detect the matrix size. As usually the max nonzero values are in the central cross of matrix.
0 0 2 0 0
0 1 2 1 0
2 2 3 2 2
0 1 2 1 0
0 0 2 0 0
Values 2 are the central cross and value 3 is the mid value of convolution matrix. So start computing the #2 correlations from mid point in x and y direction. And when hit zero value you are most likely at the edge of convolution matrix. So you can use that as matrix size (unless the matrix is some weird filter ...).
Also each color channel can have its own convolution matrix so may be you should do this separately per channel (and convolution can be done in any color space not just in RGB).
There might be other approaches for this (my guts tells me this should be solvable by PCA)
If you got black area in the original image then you can use that to find the values of matrix more easily (it will significantly ease up the algebraic approach)

Java rotation of pixel array

I have tried to make an algorithm in java to rotate a 2-d pixel array(Not restricted to 90 degrees), the only problem i have with this is: the end result leaves me with dots/holes within the image.
Here is the code :
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
int xp = (int) (nx + Math.cos(rotation) * (x - width / 2) + Math
.cos(rotation + Math.PI / 2) * (y - height / 2));
int yp = (int) (ny + Math.sin(rotation) * (x - width / 2) + Math
.sin(rotation + Math.PI / 2) * (y - height / 2));
int pixel = pixels[x + y * width];
Main.pixels[xp + yp * Main.WIDTH] = pixel;
}
}
'Main.pixels' is an array connected to a canvas display, this is what is displayed onto the monitor.
'pixels' and the function itself, is within a sprite class. The sprite class grabs the pixels from a '.png' image at initialization of the program.
I've tried looking at the 'Rotation Matrix' solutions. But they are too complicated for me. I have noticed that when the image gets closer to a point of 45 degrees, the image is some-what stretched ? What is going wrong? And what is the correct code; that adds the pixels to a larger scale array(E.g. Main.pixels[]).
Needs to be java! and relative to the code format above. I am not looking for complex examples, simply because i will not understand(As said above). Simple and straight to the point, is what i am looking for.
How id like the question to be answered.
Your formula is wrong because ....
Do this and the effect will be...
Simplify this...
Id recommend...
Im sorry if im asking to much, but i have looked for an answer relative to this question, that i can understand and use. But to always either be given a rotation of 90 degrees, or an example from another programming language.
You are pushing the pixels forward, and not every pixel is hit by the discretized rotation map. You can get rid of the gaps by calculating the source of each pixel instead.
Instead of
for each pixel p in the source
pixel q = rotate(p, theta)
q.setColor(p.getColor())
try
for each pixel q in the image
pixel p = rotate(q, -theta)
q.setColor(p.getColor())
This will still have visual artifacts. You can improve on this by interpolating instead of rounding the coordinates of the source pixel p to integer values.
Edit: Your rotation formulas looked odd, but they appear ok after using trig identities like cos(r+pi/2) = -sin(r) and sin(r+pi/2)=cos(r). They should not be the cause of any stretching.
To avoid holes you can:
compute the source coordinate from destination
(just reverse the computation to your current state) it is the same as Douglas Zare answer
use bilinear or better filtering
use less then single pixel step
usually 0.75 pixel is enough for covering the holes but you need to use floats instead of ints which sometimes is not possible (due to performance and or missing implementation or other reasons)
Distortion
if your image get distorted then you do not have aspect ratio correctly applied so x-pixel size is different then y-pixel size. You need to add scale to one axis so it matches the device/transforms applied. Here few hints:
Is the source image and destination image separate (not in place)? so Main.pixels and pixels are not the same thing... otherwise you are overwriting some pixels before their usage which could be another cause of distortion.
Just have realized you have cos,cos and sin,sin in rotation formula which is non standard and may be you got the angle delta wrongly signed somewhere so
Just to be sure here an example of the bullet #1. (reverse) with standard rotation formula (C++):
float c=Math.cos(-rotation);
float s=Math.sin(-rotation);
int x0=Main.width/2;
int y0=Main.height/2;
int x1= width/2;
int y1= height/2;
for (int a=0,y=0; y < Main.height; y++)
for (int x=0; x < Main.width; x++,a++)
{
// coordinate inside dst image rotation center biased
int xp=x-x0;
int yp=y-y0;
// rotate inverse
int xx=int(float(float(xp)*c-float(yp)*s));
int yy=int(float(float(xp)*s+float(yp)*c));
// coordinate inside src image
xp=xx+x1;
yp=yy+y1;
if ((xp>=0)&&(xp<width)&&(yp>=0)&&(yp<height))
Main.pixels[a]=pixels[xp + yp*width]; // copy pixel
else Main.pixels[a]=0; // out of src range pixel is black
}

Categories

Resources