What is happening in this pixel-rendering algorithm? - java

I just went across some code on how to draw a pixel array on top of another pixel array that looks like this:
public class Bitmap {
private int[] pixels;
private int w, h;
public void draw(Bitmap b, int xp, int yp) {
int x0 = xp;
int x1 = xp+b.w;
int y0 = yp;
int y1 = yp+b.h;
if(x0 < 0) x0 = 0;
if(x1 > w) x1 = w;
if(y0 < 0) y0 = 0;
if(y1 > h) y1 = h;
for (int y = y0; y < y1; y++) {
int sp = (y - yp) * b.w - xp;
int dp = (y) * w;
for (int x = x0; x < x1; x++) {
int c = b.pixels[sp + x];
if (c < 0) pixels[dp + x] = b.pixels[sp + x];
}
}
}
}
As You can see, one is able to draw a Bitmap object on specific coordinates on top of another Bitmap.
The thing I don't get are the two for loops. I know, that the outer for loop is the y axis of the Bitmap drawn, and starts the inner for loop to draw the x axis of the Bitmap.
Now I came over this:
int sp = (y - yp) * b.w - xp;
int dp = (y) * w;
What exactly do sp and dp stand for? And what does 'c' mean later on in
int c = b.pixels[sp + x];
if (c < 0) pixels[dp + x] = b.pixels[sp + x];
?
Thanks in advance, best regards

Given the algorithm, we can guess what the original author was thinking:
sp is "source position": the start of the row in the source bitmap
dp is "destination position": the start of the row in the destination bitmap
c is "color": the source pixel value (where negative values are transparent).

Related

What's wrong with image rotation code?

The image rotates with code below, but wrong, some black dots appears on original image. I believe it's something with rotation code. Any solution? Thanks. The image dimensions is 32x32 pixels loaded on center of screen (320x240).
public class RendPanel extends JPanel {
private static final long serialVersionUID = 1L;
int widthe = 320;
int heighte = 240;
double angle = Math.toRadians(220);
double sin = Math.sin(angle);
double cos = Math.cos(angle);
double x0 = 0.5 * (widthe - 1); // point to rotate about
double y0 = 0.5 * (heighte - 1); // center of image
public static BufferedImage fbuffer;
public RendPanel(int width, int height) {
fbuffer = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
BufferedImage in = null;
try { in = ImageIO.read(new File("square.png")); } //32x32 square .png
catch (IOException e) { e.printStackTrace(); }
for (int i = 0; i < in.getWidth(); i++) {
for (int j = 0; j < in.getHeight(); j++) {
fbuffer.setRGB(i + (320 / 2) - 16, j + (240 / 2) - 16, in.getRGB(i, j));
}
}
BufferedImage neww = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < widthe; x++) {
for (int y = 0; y < heighte; y++) {
if(x >= x0 - 32 && x <= x0 + 32 && y >= y0 - 32 && y <= y0 + 32){
double a = x - x0;
double b = y - y0;
int xx = (int) (+a * cos - b * sin + x0);
int yy = (int) (+a * sin + b * cos + y0);
// plot pixel (x, y) the same color as (xx, yy) if it's in bounds
if (xx >= 0 && xx < width && yy >= 0 && yy < height) {
neww.setRGB(xx, yy, fbuffer.getRGB(x, y));
}
}
}
}
fbuffer = neww;
repaint();
setPreferredSize(new Dimension(width, height));
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(fbuffer, 0, 0, null);
}
}
A beginner's mistake (sorry).
Taking every source pixel in turn, transforming the coordinates to the destination and copying the pixel value is not the right way.because the regular input grid will not map to a regular grid, and there will be voids (and overlaps).
The correct way is to scan the destination image (so that every destination pixel is reached) and counter-transform the coordinates to fetch the pixel value from the source.
As a refinement, you can use the four neighboring pixel from where you land in the source and perform bilinear interpolation, to reduce aliasing.
Man, it's strange, because in this code it works properly!
Heres a working code:
public class RendPanel extends JPanel {
private static final long serialVersionUID = 1L;
int widthe = 320;
int heighte = 240;
int ang = 0;
double x0 = 0.5 * (widthe - 1); // point to rotate about
double y0 = 0.5 * (heighte - 1); // center of image
public static BufferedImage fbuffer;
public RendPanel(int width, int height) {
fbuffer = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
BufferedImage in = null;
try { in = ImageIO.read(new File("square.png")); } //32x32 square .png
catch (IOException e) { e.printStackTrace(); }
for (int i = 0; i < in.getWidth(); i++) {
for (int j = 0; j < in.getHeight(); j++) {
fbuffer.setRGB(i + (320 / 2) - 16, j + (240 / 2) - 16, in.getRGB(i, j));
}
}
setPreferredSize(new Dimension(width, height));
}
BufferedImage neww;
public void r(){
neww = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
double angle = Math.toRadians(ang);
double sin = Math.sin(angle);
double cos = Math.cos(angle);
for (int x = 0; x < widthe; x++) {
for (int y = 0; y < heighte; y++) {
if(x >= x0 - 32 && x <= x0 + 32 && y >= y0 - 32 && y <= y0 + 32){
double a = x - x0;
double b = y - y0;
int xx = (int) (+a * cos - b * sin + x0);
int yy = (int) (+a * sin + b * cos + y0);
// plot pixel (x, y) the same color as (xx, yy) if it's in bounds
if (xx >= 0 && xx < widthe && yy >= 0 && yy < heighte) {
neww.setRGB(x, y, fbuffer.getRGB(xx, yy));
}
}
}
}
ang++;
repaint();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(neww, 0, 0, null);
}
}
Thanks for:
https://introcs.cs.princeton.edu/java/31datatype/Rotation.java.html
EDIT:
You have to invert the vars on bf2.setRGB(x, y, fbuffer.getRGB(xx, yy)); to the rotated coordinate.

How to rotate a 2D array of pixels about it's "center"

I've been given a 2D array of pixels and I am suppose to rotate this image based off of the pixel array about it's centermost point. I've tried to implement code which rotates the image based off of the rotation matrix, but I haven't been successful so far.
Current 2D Array Rotation Code:
int width = originalImage.length;
int height = originalImage[0].length;
final double angle = 90;
int[][] array = new int[width][height];
double c = Math.cos(Math.toRadians(angle));
double s = Math.sin(Math.toRadians(angle));
int x = width / 2;
int y = height / 2;
for (int xx = 0; xx < width; xx++) {
for (int yy = 0; yy < height; yy++) {
int xp = xx - x;
int yp = yy - y;
int xa = (int)((float)((float)xp * c - (float)yp * s));
int ya = (int)((float)((float)xp * s + (float)yp * c));
xa += x;
ya += y;
xp += x;
yp += y;
if(xa < width && ya < height) array[xa][ya] = originalImage[xp][yp];
//System.out.print("\n"+xa+" "+ya);
}
}
I've also tried this:
int[][] array = new int[originalImage.length][originalImage[0].length];
int xx = 0, yy = 0;
for (int x = originalImage.length - 1; x >= 0; x--) {
xx = 0;
for (int y = 0; y < originalImage[x].length; y++) {
//System.out.println(array[yy][xx]);
//System.out.println(originalImage[y][x]);
if (y < originalImage.length && x < originalImage[x].length) {
array[yy][xx] = originalImage[y][x];
//System.out.print(array[yy][xx]);
}
xx++;
}
yy++;
}
Are there any suggestion to how I can improve my code, or how this should be done?
since you are rotating by 90 degrees you can skip a bit
// origin to center point
int xp = xx - x;
int yp = yy - y;
// rotation and origin back to (0,0)
int x_rotated = -yp + y;
int y_rotated = xp + x;
//
array[x_rotated][y_rotated] = originalImage[xx][yy];
If you want to rotate in a different direction change
int x_rotated = yp + y;
int y_rotated = -xp + x;

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);

Wave generation with the "Hugo Elias" algorithm please! Java

I appear to have hit a wall in my most recent project involving wave/ripple generation over an image. I made one that works with basic colors on a grid that works perfectly; heck, I even added shades to the colors depending on the height of the wave.
However, my overall goal was to make this effect work over an image like you would see here. I was following an algorithm that people are calling the Hugo Elias method (though idk if he truly came up with the design). His tutorial can be found here!
When following that tutorial I found his pseudo code challenging to follow. I mean the concept for the most part makes sense until I hit the height map portion over an image. The problem being the x and y offsets throw an ArrayIndexOutOfBoundsException due to him adding the offset to the corresponding x or y. If the wave is too big (i.e. in my case 512) it throws an error; yet, if it is too small you can't see it.
Any ideas or fixes to my attempted implementation of his algorithm?
So I can't really make a compile-able version that is small and shows the issue, but I will give the three methods I'm using in the algorithm. Also keep in mind that the buffer1 and buffer2 are the height maps for the wave (current and previous) and imgArray is a bufferedImage represented by a int[img.getWidth() * img.getHeight()] full of ARGB values.
Anyways here you go:
public class WaveRippleAlgorithmOnImage extends JPanel implements Runnable, MouseListener, MouseMotionListener
{
private int[] buffer1;
private int[] buffer2;
private int[] imgArray;
private int[] movedImgArray;
private static double dampening = 0.96;
private BufferedImage img;
public WaveRippleAlgorithmOnImage(BufferedImage img)
{
this.img = img;
imgArray = new int[img.getHeight()*img.getWidth()];
movedImgArray = new int[img.getHeight()*img.getWidth()];
imgArray = img.getRGB(0, 0,
img.getWidth(), img.getHeight(),
null, 0, img.getWidth());
//OLD CODE
/*for(int y = 0; y < img.getHeight(); y++)
{
for(int x = 0; x < img.getWidth(); x++)
{
imgArray[y][x] = temp[0 + (y-0)*img.getWidth() + (x-0)];
}
}*/
buffer1 = new int[img.getHeight()*img.getWidth()];
buffer2 = new int[img.getHeight()*img.getWidth()];
buffer1[buffer1.length/2] = (img.getWidth() <= img.getHeight() ? img.getWidth() / 3 : img.getHeight() / 3);
//buffer1[25][25] = 10;
back = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
//<editor-fold defaultstate="collapsed" desc="Used Methods">
#Override
public void run()
{
while(true)
{
this.update();
this.repaint();
this.swap();
}
}
//Called from Thread to update movedImgArray prior to being drawn.
private void update()
{
//This is my attempt of trying to convert his code to java.
for (int i=img.getWidth(); i < imgArray.length - 1; i++)
{
if(i % img.getWidth() == 0 || i >= imgArray.length - img.getWidth())
continue;
buffer2[i] = (
((buffer1[i-1]+
buffer1[i+1]+
buffer1[i-img.getWidth()]+
buffer1[i+img.getWidth()]) >> 1)) - buffer2[i];
buffer2[i] -= (buffer2[i] >> 5);
}
//Still my version of his code, because of the int[] instead of int[][].
for (int y = 1; y < img.getHeight() - 2; y++)
{
for(int x = 1; x < img.getWidth() - 2; x++)
{
int xOffset = buffer1[((y)*img.getWidth()) + (x-1)] - buffer1[((y)*img.getWidth()) + (x+1)];
int yOffset = buffer1[((y-1)*img.getWidth()) + (x)] - buffer1[((y+1)*img.getWidth()) + (x)];
int shading = xOffset;
//Here is where the error occurs (after a click or wave started), because yOffset becomes -512; which in turn gets
//multiplied by y... Not good... -_-
movedImgArray[(y*img.getWidth()) + x] = imgArray[((y+yOffset)*img.getWidth()) + (x+xOffset)] + shading;
}
}
//This is my OLD code that kidna worked...
//I threw in here to show you how I was doing it before I switched to images.
/*
for(int y = 1; y < img.getHeight() - 1; y++)
{
for(int x = 1; x < img.getWidth() - 1; x++)
{
//buffer2[y][x] = ((buffer1[y][x-1] +
//buffer1[y][x+1] +
//buffer1[y+1][x] +
//buffer1[y-1][x]) / 4) - buffer2[y][x];
buffer2[y][x] = ((buffer1[y][x-1] +
buffer1[y][x+1] +
buffer1[y+1][x] +
buffer1[y-1][x] +
buffer1[y + 1][x-1] +
buffer1[y + 1][x+1] +
buffer1[y - 1][x - 1] +
buffer1[y - 1][x + 1]) / 4) - buffer2[y][x];
buffer2[y][x] = (int)(buffer2[y][x] * dampening);
}
}*/
}
//Swaps buffers
private void swap()
{
int[] temp;
temp = buffer2;
buffer2 = buffer1;
buffer1 = temp;
}
//This creates a wave upon clicking. It also is where that 512 is coming from.
//512 was about right in my OLD code shown above, but helps to cause the Exeception now.
#Override
public void mouseClicked(MouseEvent e)
{
if(e.getX() > 0 && e.getY() > 0 && e.getX() < img.getWidth() && e.getY() < img.getHeight())
buffer2[((e.getY())*img.getWidth()) + (e.getX())] = 512;
}
private BufferedImage back;
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
back.setRGB(0, 0, img.getWidth(), img.getHeight(), movedImgArray, 0, img.getWidth());
g.drawImage(back, 0, 0, null);
}
}
P.S. Here are two images of the old code working.
Looking at my original pseudocode, I assume the Array Out Of Bounds error is happening when you try to look up the texture based on the offset. The problem happens because the refraction in the water is allowing us to see outside of the texture.
for every pixel (x,y) in the buffer
Xoffset = buffer(x-1, y) - buffer(x+1, y)
Yoffset = buffer(x, y-1) - buffer(x, y+1)
Shading = Xoffset
t = texture(x+Xoffset, y+Yoffset) // Array out of bounds?
p = t + Shading
plot pixel at (x,y) with colour p
end loop
The way to fix this is simply to either clamp the texture coordinates, or let them wrap. Also, if you find that the amount of refraction is too much, you can reduce it by bit-shifting the Xoffset and Yoffset values a little bit.
int clamp(int x, int min, int max)
{
if (x < min) return min;
if (x > max) return max;
return x;
}
int wrap(int x, int min, int max)
{
while (x<min)
x += (1+max-min);
while (x>max)
x -= (1+max-min);
return x;
}
for every pixel (x,y) in the buffer
Xoffset = buffer(x-1, y) - buffer(x+1, y)
Yoffset = buffer(x, y-1) - buffer(x, y+1)
Shading = Xoffset
Xoffset >>= 1 // Halve the amount of refraction
Yoffset >>= 1 // if you want.
Xcoordinate = clamp(x+Xoffset, 0, Xmax) // Use clamp() or wrap() here
Ycoordinate = clamp(y+Yoffset, 0, Ymax) //
t = texture(Xcoordinate, Ycoordinate)
p = t + Shading
plot pixel at (x,y) with colour p
end loop

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