applying window level functionality on CT images in DIOCM - java

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

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.

What is happening in this pixel-rendering algorithm?

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

Comparing images to find duplicates

I have a few (38000) picture/video files in a folder. Approximately 40% of these are duplicates which I'm trying to get rid of. My question is, how can I tell if 2 files are identical? So far I tried to use a SHA1 of the files but it turns out that many duplicates files had different hashes. This is the code I was using:
public static String getHash(File doc) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA1");
FileInputStream inStream = new FileInputStream(doc);
DigestInputStream dis = new DigestInputStream(inStream, md);
BufferedInputStream bis = new BufferedInputStream(dis);
while (true) {
int b = bis.read();
if (b == -1)
break;
}
inStream.close();
dis.close();
bis.close();
} catch (NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
}
BigInteger bi = new BigInteger(md.digest());
return bi.toString(16);
}
Can I modify this in any way? Or will I have to use a different method?
As outlined above duplicate detection can be based on a hash. However, if you want to have near duplicate detection, which means that you are searching for images that basically show the same things, but have been scaled, rotated, etc. you might need a content based image retrieval approach. There's LIRE (https://code.google.com/p/lire/), a Java library for that, and you'll find the "SimpleApplication" in the Download section. What you then can do is to
Index the first image
go to the next image I
Search for I in the index
If there are results with a score below a threshold, then mark them as duplicate
Index I
Go to (2)
Students of mine did it, it worked well, but I don't have the source code at hand. But rest assured, it's just a few lines and the simple application will get you started.
Besides using hash, if your duplicates have different sizes (because they were resized), you could compare pixel by pixel (maybe not the entire image but a sub-section of the image).
This may depend on the image format but you could compare by comparing the height and width and then go pixel by pixel using the RGB code. To make it more efficient you can decide a threshold of comparison. For example:
public class Main {
public static void main(String[] args) throws IOException {
ImageChecker i = new ImageChecker();
BufferedImage one = ImageIO.read(new File("D:/Images/460249177.jpg"));
BufferedImage two = ImageIO.read(new File("D:/Images/460249177a.jpg"));
if(one.getWidth() + one.getHeight() >= two.getWidth() + two.getHeight()) {
i.setOne(one);
i.setTwo(two);
} else {
i.setOne(two);
i.setTwo(one);
}
System.out.println(i.compareImages());
}
}
public class ImageChecker {
private BufferedImage one;
private BufferedImage two;
private double difference = 0;
private int x = 0;
private int y = 0;
public ImageChecker() {
}
public boolean compareImages() {
int f = 20;
int w1 = Math.min(50, one.getWidth() - two.getWidth());
int h1 = Math.min(50, one.getHeight() - two.getHeight());
int w2 = Math.min(5, one.getWidth() - two.getWidth());
int h2 = Math.min(5, one.getHeight() - two.getHeight());
for (int i = 0; i <= one.getWidth() - two.getWidth(); i += f) {
for (int j = 0; j <= one.getHeight() - two.getHeight(); j += f) {
compareSubset(i, j, f);
}
}
one = one.getSubimage(Math.max(0, x - w1), Math.max(0, y - h1),
Math.min(two.getWidth() + w1, one.getWidth() - x + w1),
Math.min(two.getHeight() + h1, one.getHeight() - y + h1));
x = 0;
y = 0;
difference = 0;
f = 5;
for (int i = 0; i <= one.getWidth() - two.getWidth(); i += f) {
for (int j = 0; j <= one.getHeight() - two.getHeight(); j += f) {
compareSubset(i, j, f);
}
}
one = one.getSubimage(Math.max(0, x - w2), Math.max(0, y - h2),
Math.min(two.getWidth() + w2, one.getWidth() - x + w2),
Math.min(two.getHeight() + h2, one.getHeight() - y + h2));
f = 1;
for (int i = 0; i <= one.getWidth() - two.getWidth(); i += f) {
for (int j = 0; j <= one.getHeight() - two.getHeight(); j += f) {
compareSubset(i, j, f);
}
}
System.out.println(difference);
return difference < 0.1;
}
public void compareSubset(int a, int b, int f) {
double diff = 0;
for (int i = 0; i < two.getWidth(); i += f) {
for (int j = 0; j < two.getHeight(); j += f) {
int onepx = one.getRGB(i + a, j + b);
int twopx = two.getRGB(i, j);
int r1 = (onepx >> 16);
int g1 = (onepx >> 8) & 0xff;
int b1 = (onepx) & 0xff;
int r2 = (twopx >> 16);
int g2 = (twopx >> 8) & 0xff;
int b2 = (twopx) & 0xff;
diff += (Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1
- b2)) / 3.0 / 255.0;
}
}
double percentDiff = diff * f * f / (two.getWidth() * two.getHeight());
if (percentDiff < difference || difference == 0) {
difference = percentDiff;
x = a;
y = b;
}
}
public BufferedImage getOne() {
return one;
}
public void setOne(BufferedImage one) {
this.one = one;
}
public BufferedImage getTwo() {
return two;
}
public void setTwo(BufferedImage two) {
this.two = two;
}
}
You need to use aHash, pHash and best of both dHash algorithm for this.
I wrote a pure java library just for this few days back. You can feed it with directory path(includes sub-directory), and it will list the duplicate images in list with absolute path which you want to delete. Alternatively, you can use it to find all unique images in a directory too.
It used awt api internally, so can't be used for Android though. Since, imageIO has problem reading alot of new types of images, i am using twelve monkeys jar which is internally used.
https://github.com/srch07/Duplicate-Image-Finder-API
Jar with dependencies bundled internally can be downloaded from, https://github.com/srch07/Duplicate-Image-Finder-API/blob/master/archives/duplicate_image_finder_1.0.jar
The api can find duplicates among images of different sizes too.
You could convert your files with e.g. imagemagick convert to a format which has a canonical representation and as little metadata as possible. I guess I'd use PNM. So try something like this:
convert input.png pnm:- | md5sum -
If this does yield the same result for two files which compared different before, then metadata is in fact the source of your problem, and you can either use some command line approach like this, or update your code to read the image and compute the hash from the raw uncompressed data.
If, on the other hand, different files still compare different, then you have some changes to the actual image data. One possible cause might be the addition or removal of an alpha channel, particularly if you are dealing with PNG here. With JPEG, on the other hand, you'll likely have images uncompressed and then recompressed again, which will lead to slight modifications and data loss. JPEG is an inherently lossy codec, and any two images will likely differ unless they were created using the same application (or library), with the same settings and from the same input data. In that case you'll need to perform a fuzzy image matching. Tools like Geeqie can perform such things. If you want to do this yourself, you'll have a lot of work ahead of you, and should do some research up front.
It's been a long time so I should probably explain how I finally solved my problem. The real trick was to not use hashes to begin with and instead just compare the timestamps in the exif data. Given that these pictures were taken either by me of my wife it would have been quite unlikely for different files to have the same timestamp, hence this simpler solution was actually much more reliable.
You can check different percentage of two images through below method and if different percentage os below 10 then you can call it identical image:
private static double getDifferencePercent(BufferedImage img1, BufferedImage img2) {
int width = img1.getWidth();
int height = img1.getHeight();
int width2 = img2.getWidth();
int height2 = img2.getHeight();
if (width != width2 || height != height2) {
throw new IllegalArgumentException(String.format("Images must have the same dimensions: (%d,%d) vs. (%d,%d)", width, height, width2, height2));
}
long diff = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
diff += pixelDiff(img1.getRGB(x, y), img2.getRGB(x, y));
}
}
long maxDiff = 3L * 255 * width * height;
return 100.0 * diff / maxDiff;
}
private static int pixelDiff(int rgb1, int rgb2) {
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = rgb1 & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = rgb2 & 0xff;
return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2);
}
// covert image to Buffered image through this method
public static BufferedImage toBufferedImage(Image img)
{
if (img instanceof BufferedImage)
{
return (BufferedImage) img;
}
// Create a buffered image with transparency
BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
// Draw the image on to the buffered image
Graphics2D bGr = bimage.createGraphics();
bGr.drawImage(img, 0, 0, null);
bGr.dispose();
// Return the buffered image
return bimage;
}
Get insight idea from this site : https://rosettacode.org/wiki/Percentage_difference_between_images#Kotlin
The question was asked long time ago. I have found the following link very useful, it has codes for all languages. https://rosettacode.org/wiki/Percentage_difference_between_images#Kotlin
Here is the code for Kotlin taken from the link
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
import kotlin.math.abs
fun getDifferencePercent(img1: BufferedImage, img2: BufferedImage): Double {
val width = img1.width
val height = img1.height
val width2 = img2.width
val height2 = img2.height
if (width != width2 || height != height2) {
val f = "(%d,%d) vs. (%d,%d)".format(width, height, width2, height2)
throw IllegalArgumentException("Images must have the same dimensions: $f")
}
var diff = 0L
for (y in 0 until height) {
for (x in 0 until width) {
diff += pixelDiff(img1.getRGB(x, y), img2.getRGB(x, y))
}
}
val maxDiff = 3L * 255 * width * height
return 100.0 * diff / maxDiff
}
fun pixelDiff(rgb1: Int, rgb2: Int): Int {
val r1 = (rgb1 shr 16) and 0xff
val g1 = (rgb1 shr 8) and 0xff
val b1 = rgb1 and 0xff
val r2 = (rgb2 shr 16) and 0xff
val g2 = (rgb2 shr 8) and 0xff
val b2 = rgb2 and 0xff
return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2)
}
fun main(args: Array<String>) {
val img1 = ImageIO.read(File("Lenna50.jpg"))
val img2 = ImageIO.read(File("Lenna100.jpg"))
val p = getDifferencePercent(img1, img2)
println("The percentage difference is ${"%.6f".format(p)}%")
}

x and y Offseting / Bit shifting

I'm really confused with this method, mainly because I'm confused why he bit shifts in some parts.
I have no idea why a map width mask is used or anything here its all so confusing, can someone dissect this all for me?
public static final int MAP_WIDTH = 64;
public static final int MAP_WIDTH_MASK = MAP_WIDTH - 1;
public int[] tiles = new int[MAP_WIDTH * MAP_WIDTH];
public int[] colours = new int[MAP_WIDTH * MAP_WIDTH * 4];
public int xOffSet = 0;
public int yOffSet = 0;
public int width;
public int height;
public void render(int[] pixels, int offset, int row) {
for(int yTile = yOffSet >> 3; yTile <= (yOffSet +height) >> 3; yTile++) {
int yMin = yTile * 8 - yOffSet;
int yMax = yMin + 8;
if(yMin <0) yMin = 0;
if(yMax > height) yMax = height;
for(int xTile = xOffSet >> 3; xTile <= (xOffSet + width) >> 3; xTile++) {
int xMin = xTile * 8 - xOffSet;
int xMax = xMin + 8;
if(xMin <0) xMin = 0;
if(xMax > width) xMax = width;
int tileIndex = (xTile & (MAP_WIDTH_MASK)) + (yTile & (MAP_WIDTH_MASK)) * MAP_WIDTH;
for(int y= yMin; y<yMax; y++) {
int sheetPixel = ((y + yOffSet) & 7) * sheet.width + ((xMin + xOffSet) & 7 );
int tilePixel = offset + xMin + y * row;
for(int x = xMin; x < xMax; x++) {
int colour = tileIndex * 4 + sheet.pixels[sheetPixel++];
pixels[tilePixel++] = colours[colour];
}
}
}
}
}
For the xOffset >> 3 sorts of things, it appears he's just trying to divide by 8 (and doesn't know how to make readable code). As for why he's masking all of the bits except the last 3, I couldn't say without knowing more about the code.
This will probably help:
y >> 3 is equivalent to y / 8 rounded down; it moves everything over to erase the last three bits.
y * 8 is the equivalent to y << 3. Beats me why he didn't stay consistent between the two of them.
y & 7 is the equivalent to y % 8; it keeps only the last three bits.
You didn't mention sheet in your description, but it looks like you're working with "tiles" of 8 pixels by 8 pixels on a map of 64 pixels by 64 pixels. One thing that may help is a refactoring tool--see if you can extract some methods that make sense.

RGB to CIELAB conversion [duplicate]

This question already has answers here:
Java: how to convert RGB color to CIE Lab
(6 answers)
Closed 6 years ago.
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.ImageIO;
public class ConvertRGBtoLAB {
public static void main(String[] args) {
//get input image
String fileName = "IMG_7990.jpg";
//read input image
BufferedImage image = null;
try
{
image = ImageIO.read(new File(fileName));
}
catch (IOException e)
{
e.printStackTrace();
}
//setup result image
int sizeX = image.getWidth();
int sizeY = image.getHeight();
float r, g, b, X, Y, Z, fx, fy, fz, xr, yr, zr;
float ls, as, bs;
float eps = 216.f/24389.f;
float k = 24389.f/27.f;
float Xr = 0.964221f; // reference white D50
float Yr = 1.0f;
float Zr = 0.825211f;
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int c = image.getRGB(x,y);
int R= (c & 0x00ff0000) >> 16;
int G = (c & 0x0000ff00) >> 8;
int B = c & 0x000000ff;
r = R/255.f; //R 0..1
g = G/255.f; //G 0..1
b = B/255.f; //B 0..1
// assuming sRGB (D65)
if (r <= 0.04045)
r = r/12;
else
r = (float) Math.pow((r+0.055)/1.055,2.4);
if (g <= 0.04045)
g = g/12;
else
g = (float) Math.pow((g+0.055)/1.055,2.4);
if (b <= 0.04045)
b = b/12;
else
b = (float) Math.pow((b+0.055)/1.055,2.4);
X = 0.436052025f*r + 0.385081593f*g + 0.143087414f *b;
Y = 0.222491598f*r + 0.71688606f *g + 0.060621486f *b;
Z = 0.013929122f*r + 0.097097002f*g + 0.71418547f *b;
// XYZ to Lab
xr = X/Xr;
yr = Y/Yr;
zr = Z/Zr;
if ( xr > eps )
fx = (float) Math.pow(xr, 1/3.);
else
fx = (float) ((k * xr + 16.) / 116.);
if ( yr > eps )
fy = (float) Math.pow(yr, 1/3.);
else
fy = (float) ((k * yr + 16.) / 116.);
if ( zr > eps )
fz = (float) Math.pow(zr, 1/3.);
else
fz = (float) ((k * zr + 16.) / 116);
ls = ( 116 * fy ) - 16;
as = 500*(fx-fy);
bs = 200*(fy-fz);
int Ls = (int) (2.55* ls + .5);
int As = (int) (as + .5);
int Bs = (int) (bs + .5);
int lab = 0xFF000000 + (Ls << 16) + (As << 8) + Bs; // and reassign
image.setRGB(x, y, lab);
}
}
//write new image
File outputfile = new File("lab.png");
try {
// png is an image format (like gif or jpg)
ImageIO.write(image, "png", outputfile);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Hello,
I am trying to turn a RGB image into CIELAB colour space(LAB) I get an output but I have no idea what it is supposed to look like.
Can anyone point me to a already existing image converter or confirm that I have done this correctly?
Thanks!
I personally use this site as quick reference on conversion formulas between common color spaces.
OpenCV have functions for conversions between different color spaces. Look at my other answer here. This is in C, bout you can easily check you code.
You will want to ensure that Ls, As, and Bs are clamped to the range 0 to 255. The statement you have to combine them into a single int will allow an out-of-bounds value to affect the other values.

Categories