Convert saturate_cast<uchar> in c++ to Java - java

I would like to know how to convert the following C++ code into java.
In particular, I need to convert RGB images into Opponent Space Images in order to extract the features for matching in my AR application.
void rgb2OpponentColorspace(Mat &bgrImage, vector<Mat> &opponentImageChannels)
{
if(bgrImage.type() != CV_8UC3)
{
cerr << "Error, attempting to convert to opponent space a non-BGR image!\n";
return;
}
//opponentImageChannels.resize(3);
opponentImageChannels.push_back(Mat(bgrImage.size(),CV_8UC1));
opponentImageChannels.push_back(Mat(bgrImage.size(),CV_8UC1));
opponentImageChannels.push_back(Mat(bgrImage.size(),CV_8UC1));
for(int y = 0; y < bgrImage.rows; ++y)
{
for(int x = 0; x < bgrImage.cols; ++x)
{
Vec3b v = bgrImage.at<Vec3b>(y, x);
uchar& b = v[0];
uchar& g = v[1];
uchar& r = v[2];
opponentImageChannels[0].at<uchar>(y, x) = saturate_cast<uchar>(0.5f * (255 + g - r)); // (R - G)/sqrt(2), but converted to the destination data type
opponentImageChannels[1].at<uchar>(y, x) = saturate_cast<uchar>(0.25f * (510 + r + g - 2*b)); // (R + G - 2B)/sqrt(6), but converted to the destination data type
opponentImageChannels[2].at<uchar>(y, x) = saturate_cast<uchar>(1.f/3.f * (r + g + b)); // (R + G + B)/sqrt(3), but converted to the destination data type
}

You may try implement this function by yourself, like this:
private final static int MAX_UCHAR = 255;
public static int saturateCastUchar(int x) {
return x > MAX_UCHAR ? MAX_UCHAR : (x < 0 ? 0 : x);
}
public static int saturateCastUchar(float x) {
return (int) (x > MAX_UCHAR ? MAX_UCHAR : (x < 0 ? 0 : x));
}
OpenCV for Java doesn't contain any ready-for-use function for this.

I have some discussion with my senior and we come out with the following solution.
opponentImageChannels.add(new Mat(mReferenceBGRImage.size(), CvType.CV_32FC1));
opponentImageChannels.add(new Mat(mReferenceBGRImage.size(), CvType.CV_32FC1));
opponentImageChannels.add(new Mat(bgrImage.size(), CvType.CV_32FC1));
for (int y = 0; y < bgrImage.rows(); ++y) {
for (int x = 0; x < bgrImage.cols(); ++x) {
double[] data = bgrImage.get(y, x);
double b = data[0];
double g = data[1];
double r = data[2];
opponentImageChannels.get(0).put(y, x, 0.5f * (255 + g + r));
opponentImageChannels.get(1).put(y, x, 0.25 * (510 + r + g - 2 * b));
opponentImageChannels.get(2).put(y, x, 0.5f * 1.0f / 3.0f * (r + g + b));
}
1) By using .add to replace .pushback
2) By using .get to replace .at
3) By using .put to place something into the channel

Related

Inconsistent results from taking average of two RGB values

Why are those pixel rgb values sometimes equal and sometimes not equal? I am learning image processing. It would be great if someone help me out here.
public class ColorTest1 {
Color p1;
Color p2;
ColorTest1() throws IOException, InterruptedException {
BufferedImage bi = ImageIO.read(new File("d:\\x.jpg"));
for (int y = 0; y < bi.getHeight(); y++) {
for (int x = 0; x < bi.getWidth() - 1; x++) {
p1 = new Color(bi.getRGB(x, y));
p2 = new Color(bi.getRGB(x + 1, y));
int a = (p1.getAlpha() + p2.getAlpha()) / 2;
int r = (p1.getRed() + p2.getRed()) / 2;
int g = (p1.getGreen() + p2.getGreen()) / 2;
int b = (p1.getBlue() + p2.getBlue()) / 2;
int x1 = p1.getRGB();
int x2 = p2.getRGB();
int sum1 = (x1 + x2) / 2;
int sum2 = a * 16777216 + r * 65536 + g * 256 + b;
System.out.println(sum1 == sum2);
}
}
}
public static void main(String... areg) throws IOException, InterruptedException {
new ColorTest1();
}
}
This is the image:
Take two pixels. One is black. The other is nearly black but with a slight bit of red in it, just 1/255. Ignore alpha. r will be (0 + 1) / 2 = 0. g and b will be 0 too. x1 will be 0. x2 will be 65536, right? So sum1 will be 65536 / 2 = 32768. sum2 obviously will be 0.
Whenever the sum of either red or green of the two colours is odd, the int division will set the high bit of the next colour in RGB, leading to an unexpected result.

Java: implementation of Gaussian Blur

I need to implement Gaussian Blur in Java for 3x3, 5x5 and 7x7 matrix. Can you correct me if I'm wrong:
I've a matrix(M) 3x3 (middle value is M(0, 0)):
1 2 1
2 4 2
1 2 1
I take one pixel(P) from image and for each nearest pixel:
s = M(-1, -1) * P(-1, -1) + M(-1, 0) * P(-1, 0) + ... + M(1, 1) * P(1, 1)
An then division it total value of matrix:
P'(i, j) = s / M(-1, -1) + M(-1, 0) + ... + M(1, 1)
That's all that my program do. I leave extreme pixels not changed.
My program:
for(int i = 1; i < height - 1; i++){
for(int j = 1; j < width - 1; j++){
int sum = 0, l = 0;
for(int m = -1; m <= 1; m++){
for(int n = -1; n <= 1; n++){
try{
System.out.print(l + " ");
sum += mask3[l++] * Byte.toUnsignedInt((byte) source[(i + m) * height + j + n]);
} catch(ArrayIndexOutOfBoundsException e){
int ii = (i + m) * height, jj = j + n;
System.out.println("Pixels[" + ii + "][" + jj + "] " + i + ", " + j);
System.exit(0);
}
}
System.out.println();
}
System.out.println();
output[i * width + j] = sum / maskSum[0];
}
}
I get source from a BufferedImage like this:
int[] source = image.getRGB(0, 0, width, height, null, 0, width);
So for this image:
Result is this:
Can you describe me, what is wrong with my program?
First of all, your formula for calculating the index in the source array is wrong. The image data is stored in the array one pixel row after the other. Therefore the index given x and y is calculated like this:
index = x + y * width
Furthermore the color channels are stored in different bits of the int cannot simply do the calculations with the whole int, since this allows channels to influence other channels.
The following solution should work (even though it just leaves the pixels at the bounds transparent):
public static BufferedImage blur(BufferedImage image, int[] filter, int filterWidth) {
if (filter.length % filterWidth != 0) {
throw new IllegalArgumentException("filter contains a incomplete row");
}
final int width = image.getWidth();
final int height = image.getHeight();
final int sum = IntStream.of(filter).sum();
int[] input = image.getRGB(0, 0, width, height, null, 0, width);
int[] output = new int[input.length];
final int pixelIndexOffset = width - filterWidth;
final int centerOffsetX = filterWidth / 2;
final int centerOffsetY = filter.length / filterWidth / 2;
// apply filter
for (int h = height - filter.length / filterWidth + 1, w = width - filterWidth + 1, y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int r = 0;
int g = 0;
int b = 0;
for (int filterIndex = 0, pixelIndex = y * width + x;
filterIndex < filter.length;
pixelIndex += pixelIndexOffset) {
for (int fx = 0; fx < filterWidth; fx++, pixelIndex++, filterIndex++) {
int col = input[pixelIndex];
int factor = filter[filterIndex];
// sum up color channels seperately
r += ((col >>> 16) & 0xFF) * factor;
g += ((col >>> 8) & 0xFF) * factor;
b += (col & 0xFF) * factor;
}
}
r /= sum;
g /= sum;
b /= sum;
// combine channels with full opacity
output[x + centerOffsetX + (y + centerOffsetY) * width] = (r << 16) | (g << 8) | b | 0xFF000000;
}
}
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
result.setRGB(0, 0, width, height, output, 0, width);
return result;
}
int[] filter = {1, 2, 1, 2, 4, 2, 1, 2, 1};
int filterWidth = 3;
BufferedImage blurred = blur(img, filter, filterWidth);

applying window level functionality on CT images in DIOCM

I am trying to implement window level functionality( To apply bone, brain, lung etc on CT) for DICOM images in my application and implemented formula as per the DICOM specification.
I am changing pixel values based on below formula and creating a new image, but images are becoming blank. What am doing wrong and is this correct way to do this. Please help :(:( Thanks
BufferedImage image = input image;
double w = 2500; // Window width
double c = 500; // window Center
double ymin = 0;
double ymax = 255;
double x = 0;
double y = 0;
double slope = dicomObject.get(Tag.RescaleSlope).getFloat(true);
double intercept = dicomObject.get(Tag.RescaleIntercept).getFloat(true);
int width = image.getWidth();
int height = image.getHeight();
double val = c - 0.5 - (w - 1) / 2;
double val2 = c - 0.5 + (w - 1) / 2;
for (int m = 0; m < height; m++) {
for (int n = 0; n < width; n++) {
int rgb = image.getRGB(n, m);
int valrgb = image.getRGB(n, m);
int a = (0xff000000 & valrgb) >>> 24;
int r = (0x00ff0000 & valrgb) >> 16;
int g = (0x0000ff00 & valrgb) >> 8;
int b = (0x000000ff & valrgb);
x = a + r + g + b;
if (x <= val)
y = ymin;
else if (x > val2)
y = ymax;
else {
y = ((x - (c - 0.5)) / (w - 1) + 0.5) * (ymax - ymin)+ ymin;
}
y = y * slope + intercept;
rgb = (int) y;
image.setRGB(n, m, rgb);
}
}
String filePath = "out put fileName";
ImageIO.write(image, "jpeg", new File(filePath));
First of all whats in your BufferedImage image ?
There are three steps you want to take from raw (decopressed) pixel data:
Get stored values - apply BitsAllocated, BitsStored, HighBit transformation. (I guess you image already passed that level)
Get modality values - thats your Slope, Intercept transformation. Ofter this transformation, your data will be in Hounsfield Units for CT.
Then you apply WW/WL (Value Of Interest) transformation, which will transform this window of walues into grayscale color space.
EDIT:
You've got to tell me where did you get "input image" from? After decompression pixel data should be in a byte array of size byte[width*height*2] (for CT Image BitsAllocated is always 16, thus *2). You can get stored values like this:
ushort code = (ushort)((pixel[0] + (pixel[1] << 8)) & (ushort)((1<<bitsStored) - 1));
int value = TwosComplementDecode(code);

Encoding yuv frames to video file in java

I am trying to encode a video in java.
I have access to the separate frames as I420 yuv frames (these come from a different part of the program that I cannot change).
I basically have 3 bytebuffers for the different planes of a frame (+ dimensions).
As far as I understand, my format has 1 byte for the y-plane, and half a byte for u and v each, per pixel.
What is the best way to encode these into an mp4 video file?
I have tried with the xuggler API, but I can't seem to find a way to use the yuv frames directly.
Right now, I would convert them to a BufferedImage (TYPE_3BYTE_BGR) first before I can use them with the xuggler api to encode them to a video.
But this creates a huge overhead (I have to convert the yuv data to rgb for each pixel) and is unnecessary, as xuggler encodes them to yuv frames again to store them in a video file? (Not sure about this.)
So is there any easier way to encode raw yuv-frames to a video file directly in java?
Thanks for any pointers.
The way your are planning this seems correct (manual conversion), as long as xuggler didn't re-encode the frame after you.
I have done this conversion with both python and C, the process is still the same as yours (frame by frame, looping the pixels). In Java, this could look like :
public class YUV2RGB
{
public static void convert(int[] argb, byte[] yuv, int width, int height)
{
final int frameSize = width * height;
final int ii = 0;
final int ij = 0;
final int di = +1;
final int dj = +1;
int a = 0;
int y, v, u, r, g, b;
for (int i = 0, ci = ii; i < height; ++i, ci += di)
{
for (int j = 0, cj = ij; j < width; ++j, cj += dj)
{
y = (0xff & ((int) yuv[ci * width + cj]));
v = (0xff & ((int) yuv[frameSize + (ci >> 1) * width
+ (cj & ~1) + 0]));
u = (0xff & ((int) yuv[frameSize + (ci >> 1) * width
+ (cj & ~1) + 1]));
y = y < 16 ? 16 : y;
// METHOD 1 [slower, less accurate]
/*
* r = y + (int) 1.402f * v; g = y - (int) (0.344f * u + 0.714f
* * v); b = y + (int) 1.772f * u; r = r > 255 ? 255 : r < 0 ? 0
* : r; g = g > 255 ? 255 : g < 0 ? 0 : g; b = b > 255 ? 255 : b
* < 0 ? 0 : b; argb[a++] = 0xff000000 | (b<<16) | (g<<8) | r;
*/
// METHOD 2
r = (int) (1.164f * (y - 16) + 1.596f * (v - 128));
g = (int) (1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
b = (int) (1.164f * (y - 16) + 2.018f * (u - 128));
r = r < 0 ? 0 : (r > 255 ? 255 : r);
g = g < 0 ? 0 : (g > 255 ? 255 : g);
b = b < 0 ? 0 : (b > 255 ? 255 : b);
argb[a++] = 0xff000000 | (r << 16) | (g << 8) | b;
}
}
}
}
Sample code from https://github.com/jyanik/Mocobar/blob/master/Mocobar/src/com/yanik/mocobar/camera/YUV2RGB.java
I can´t remember the source of this code, probably it was from a question here in SO, but comes in handy here as it´s a version of the above using integer maths, I needed it for an Android project!
//Method from Ketai project! Not mine! See below...
void decodeYUV420SP(int[] 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);
}
}
}

Rotate Bitmap pixels

I'm trying to rotate a Bitmap where the pixels are stored in an Array int pixels[]. I got the following method:
public void rotate(double angle) {
double radians = Math.toRadians(angle);
double cos, sin;
cos = Math.cos(radians);
sin = Math.sin(radians);
int[] pixels2 = pixels;
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++) {
int centerx = this.width / 2, centery = this.height / 2;
int m = x - centerx;
int n = y - centery;
int j = (int) (m * cos + n * sin);
int k = (int) (n * cos - m * sin);
j += centerx;
k += centery;
if (!((j < 0) || (j > this.width - 1) || (k < 0) || (k > this.height - 1)))
try {
pixels2[(x * this.width + y)] = pixels[(k * this.width + j)];
} catch (Exception e) {
e.printStackTrace();
}
}
pixels = pixels2;
}
But it just gives me crazy results. Does anyone know where the error is?
The line
int[] pixels2 = pixels;
is supposed to copy the array, but you are just copying the reference to it. Use pixels.clone(). In fact, you just need a new, empty array, so new int[pixels.lenght] is enough. In the end you need System.arraycopy to copy the new content into the old array.
There are other problems in your code -- you are mixing up rows and columns. Some expressions are written as though the image is stored row by row, others as if column by column. If row-by-row (my assumption), then this doesn't make sense: x*width + y. It should read y*width + x -- you are skipping y rows down and then moving x columns to the right. All in all, I have this code that works OK:
import static java.lang.System.arraycopy;
public class Test
{
private final int width = 5, height = 5;
private int[] pixels = {0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0};
public Test rotate(double angle) {
final double radians = Math.toRadians(angle),
cos = Math.cos(radians), sin = Math.sin(radians);
final int[] pixels2 = new int[pixels.length];
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++) {
final int
centerx = this.width / 2, centery = this.height / 2,
m = x - centerx,
n = y - centery,
j = ((int) (m * cos + n * sin)) + centerx,
k = ((int) (n * cos - m * sin)) + centery;
if (j >= 0 && j < width && k >= 0 && k < this.height)
pixels2[(y * width + x)] = pixels[(k * width + j)];
}
arraycopy(pixels2, 0, pixels, 0, pixels.length);
return this;
}
public Test print() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
System.out.print(pixels[width*y + x]);
System.out.println();
}
System.out.println();
return this;
}
public static void main(String[] args) {
new Test().print().rotate(-45).print();
}
}
public void render(float nx, float ny, float nz, float size, float rotate) {
int wid = (int) ((width - nz) * size);
int hgt = (int) ((height - nz) * size);
if (wid < 0 || hgt < 0) {
wid = 0;
hgt = 0;
}
for (int x = 0; x < wid; x++) {
for (int y = 0; y < hgt; y++) {
double simple = Math.PI;
int xp = (int) (nx +
Math.cos(rotate) * ((x / simple) - (wid / simple) / 2) + Math
.cos(rotate + Math.PI / 2)
* ((y / simple) - (hgt / simple) / 2));
int yp = (int) (ny + Math.sin(rotate)
* ((x / simple) - (wid / simple) / 2) + Math.sin(rotate
+ Math.PI / 2)
* ((y / simple) - (hgt / simple) / 2));
if (xp + width < 0 || yp + height < 0 || xp >= Main.width
|| yp >= Main.height) {
break;
}
if (xp < 0
|| yp < 0
|| pixels[(width / wid) * x + ((height / hgt) * y)
* width] == 0xFFFF00DC) {
continue;
}
Main.pixels[xp + yp * Main.width] = pixels[(width / wid) * x
+ ((height / hgt) * y) * width];
}
}
}
This is only a new to rotating for me, but the process of this is that of a normal rotation. It still needs much fixing -- it's inefficient and slow. But in a small program, this code works. I'm posting this so you can take it, and make it better. :)

Categories