I want to change the slice thickness of my dicom volume data. I'm using
vtkImageViewer2.
For example, the original data spacing is 2 and there are 200 slices, when I
change the slice thickness value to 4 I have to see 100 slices.
Original: 1,2,3,4,5...
Modified: 1, 2, 3...
My code:
if ((modif & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {
etat = 1;
int nb0 = imageViewer.GetSlice() + 1;
int nb1 = imageViewer.GetSlice() - 1;
int totSlice = imageViewer.GetSliceMax() + 1;
if (p1.y > p2.y) {
String Newligne=System.getProperty("line.separator");
cornerAnnotation.SetText(0,"Slice:" + (nb0 + 1) + "/" + totSlice+Newligne+"Zoom: "+(int)(100)+"%"+Newligne+ "C:" + windowhight + " / W:" +windowlevel+ Newligne+"Pixel:("+xs+":"+ys+")"+Newligne+reader.GetModality()+"("+reader.GetOutput().GetDimensions()[0]+"*"+reader.GetOutput().GetDimensions()[1]+")"+"-Axial"+Newligne);
imageViewer.SetSlice(nb0);
scrollBar.setValue(imageViewer.GetSlice());
} else {
String Newligne=System.getProperty("line.separator");
cornerAnnotation.SetText(0,"Slice:" + (nb1 + 1) + "/" + totSlice+Newligne+"Zoom: "+(int)(100)+"%"+Newligne+ "C:" + windowhight + " / W:" +windowlevel+ Newligne+"Pixel:("+xs+":"+ys+")"+Newligne+reader.GetModality()+"("+reader.GetOutput().GetDimensions()[0]+"*"+reader.GetOutput().GetDimensions()[1]+")"+"-Axial"+Newligne);
imageViewer.SetSlice(nb1);
scrollBar.setValue(imageViewer.GetSlice());
}
}
If you actually change the slice thickness in the DICOM attributes, you likely will have to change the image position (patient) and slice location DICOM attributes as well in order to keep the image volume consistent.
If you are just trying to move slices based on a certain distance (e.g. one click = 4 mm instead of 2 mm), then keep track of the position of the slice instead of the slice number. When the position changes, then compute the new slice for the new position and update to that slice. This will allow more flexibility as well.
If you really just want to step every other slice, then why not just use nb0 = getSlice() + 2 and nb1 = getSlice() -2?
Related
I'm writing some intelligence for a virtual rover driving around on Mars picking up resources. I have the following code:
public Point getPointFromRoverOffset(double offsetX, double offsetY) {
double x = offsetX + currentLocation.x;
double y = offsetY + currentLocation.y;
if(x > getWorldWidth()) {
x = x - getWorldWidth();
}
else if (x < 0) {
x = getWorldWidth() + x;
}
if(y > getWorldHeight()) {
y = y - getWorldHeight();
}
else if(y < 0) {
y = getWorldHeight() + y;
}
getLog().info("Based on location " + currentLocation.toString());
getLog().info("Decided that offset (" + offsetX + "," + offsetY + ") = (" + x + "," + y + ")");
return new Point(x, y);
}
All the numbers involved are doubles, representing a 2d vector in a 2d plane.
getWorldWidth() and getWorldHeight() both return 20.0
Im getting the following strange results:
[INFO] 16:41 Versatile - Based on location (0.0,6.0)
[INFO] 16:41 Versatile - Decided that offset (0.0,-5.999999999999999) = (0.0,8.881784197001252E-16)
Seemingly the input Y value -5.9 (recurring, a double rounding fault), and the current Y position 0.6 so the value should have been 0.1. Yet it comes out as 8.88 (et al).
Why? Is this some odd behaviour of doubles that I'm not aware of? Or am I missing something more obvious?
8.881784197001252E-16 is a very small number. it is approx 0.000000000000000888 or 8.88*10^-16. It is the difference between the value with a slight rounding error and the expect value.
If you need values to be exact, I suggest either
round the doubles
use a small allowed error in your calculations e.g. ERR = 1e-6
use integers (by scaling everything by 1000 for example)
use BigDecimal.
I am working on a 3D LibGDX project, and am trying to manipulate the vertices of a 3D model. I have the model loading, and I have this as my code:
Model plain = assets.get("plain.g3db", Model.class);
for(Mesh m : plain.meshes){
float[] t = m.getVertices(new float[m.getMaxVertices()]);
float[] newVerticies = new float[m.getMaxVertices()];
for(int i = 0; i < t.length-1; i++){
newVerticies[i] = t[i];
System.out.println("X: " + t[i] + " " + i);
newVerticies[i] = t[i];
System.out.println("Y: " + t[i++] + " " + i);
newVerticies[i] = random.nextInt(1-0) + 0;
System.out.println("Z: " + t[i++] + " " + i);
newVerticies[i] = t[i];
System.out.println("R: " + t[i++]);
newVerticies[i] = t[i];
System.out.println("G: " + t[i++]);
newVerticies[i] = t[i];
System.out.println("B: " + t[i++]);
newVerticies[i] = t[i];
System.out.println("A: " + t[i++]);
}
m.setVertices(newVerticies);
}
That does not work how I want it to, but I can at least see the model. If I comment out these lines:
newVerticies[i] = t[i];
System.out.println("R: " + t[i++]);
newVerticies[i] = t[i];
System.out.println("G: " + t[i++]);
newVerticies[i] = t[i];
System.out.println("B: " + t[i++]);
newVerticies[i] = t[i];
System.out.println("A: " + t[i++]);
I just get a black screen. Even if I move around, I see nothing. What I want to know, is what exactly float[] t = m.getVertices(new float[m.getMaxVertices()]); outputs. How does the output correspond to the model? How can I make the Y value random within a range?
Mesh data is organized into VertexAttributes. These always include position, but can also include color, texture coordinates, normals, etc. For example, a mesh with a texture might have the following:
A Position VertexAttribute of size 3 for X, Y, and Z (it's possible to have a 2D mesh where the position attribute has only 2)
A TextureCooridantes VertexAttribute of size 2 for U and V
A Normal VertexAttribute with size 3 for X, Y, and Z
So the float array you get with mesh.getVertices() would be this set of 8 floats for each vertex, one after the other.
If you loaded your Mesh from a model file rather than constructing it manually, you might not be sure of what VertexAttribute setup it has, so you need to inspect it to find out what the offset of the attribute you want is:
int positionOffset = mesh.getVertexAttributes().getOffset(Usage.Position);
int yOffset = positionOffset + 1; //skip X
int vertexSize = mesh.getVertexSize() / 4; //divide to convert bytes to floats
Now if you wanted to change Y, you would loop through the vertices something like this:
//this is how to get the properly sized mesh:
float[] vertices = new float[mesh.getNumVertices() * mesh.getVertexSize() / 4];
mesh.getVertices(vertices);
for (int i = yOffset; i < vertices.length; i += vertexSize){
vertices[i] += someValue;
}
mesh.setVertices(vertices);
The indices indicate groups of three vertices that make up the triangles of the mesh. If there are vertices on your mesh that are the same on multiple triangles, they might appear in the vertices list only once. This typically happens on soft-shaded vertices of a mesh, since the UV and normal will be the same for all the adjacent triangles touching that vertex.
Getting negative imageXScale and imageYScale for some of pdf's
while converting pdf to image and finding its DPI.
Jar used is pdfbox1.8.8 and iText.
Found image Im0
position=602.64,451.08
size=837px,626px size=-212.59799mm,-159.131mm
Position which must be 0 has some value.
unable to detect the problem
The OP mentions he uses pdfbox1.8.8 and iText but offers no further indication how he retrieves values from his PDF using either of these libraries.
Considering the words imageXScale and imageYScale and the position and size outputs, I would assume he has used the PrintImageLocations PDFBox example.
The meaning of the PrintImageLocations outputs
This sample does the following outputs for a bitmap image drawn somewhere on a page:
System.out.println("Found image [" + objectName.getName() + "]");
The name of the image resource
Matrix ctmNew = getGraphicsState().getCurrentTransformationMatrix();
float imageXScale = ctmNew.getScalingFactorX();
float imageYScale = ctmNew.getScalingFactorY();
// position in user space units. 1 unit = 1/72 inch at 72 dpi
System.out.println("position in PDF = " + ctmNew.getTranslateX() + ", " + ctmNew.getTranslateY() + " in user space units");
Position of the anchor point, i.e. where the original bottom left corner of the image is drawn on the page.
// raw size in pixels
System.out.println("raw image size = " + imageWidth + ", " + imageHeight + " in pixels");
The original width and height of the image resource in pixels. Always non-negative.
// displayed size in user space units
System.out.println("displayed size = " + imageXScale + ", " + imageYScale + " in user space units");
The width and height of the image as drawn on the page. Negative values may mean that the image resource is not drawn right and up from the anchor point but instead left and down.
// displayed size in inches at 72 dpi rendering
imageXScale /= 72;
imageYScale /= 72;
System.out.println("displayed size = " + imageXScale + ", " + imageYScale + " in inches at 72 dpi rendering");
The width and height of the image as drawn on the page in inches assuming a user space unit width of 1/72nd inch, the default. Negative values may occur, see above.
// displayed size in millimeters at 72 dpi rendering
imageXScale *= 25.4;
imageYScale *= 25.4;
System.out.println("displayed size = " + imageXScale + ", " + imageYScale + " in millimeters at 72 dpi rendering");
The width and height of the image as drawn on the page in mm assuming a user space unit width of 1/72nd inch, the default. Negative values may occur, see above.
Thus, negative values here have a meaning (a mirroring or 180° rotation) which makes no difference in respect to any DPI properties. So to calculate a DPI value, use the absolute values only, ignore the signs.
Inconsistency in PDFBox
The x and y scaling factors used above are derived from the current transformation matrix like this:
/**
* Returns the x-scaling factor of this matrix. This is calculated from the scale and shear.
*
* #return The x-scaling factor.
*/
public float getScalingFactorX()
{
float xScale = single[0];
/**
* BM: if the trm is rotated, the calculation is a little more complicated
*
* The rotation matrix multiplied with the scaling matrix is:
* ( x 0 0) ( cos sin 0) ( x*cos x*sin 0)
* ( 0 y 0) * (-sin cos 0) = (-y*sin y*cos 0)
* ( 0 0 1) ( 0 0 1) ( 0 0 1)
*
* So, if you want to deduce x from the matrix you take
* M(0,0) = x*cos and M(0,1) = x*sin and use the theorem of Pythagoras
*
* sqrt(M(0,0)^2+M(0,1)^2) =
* sqrt(x2*cos2+x2*sin2) =
* sqrt(x2*(cos2+sin2)) = <- here is the trick cos2+sin2 is one
* sqrt(x2) =
* abs(x)
*/
if( !(single[1]==0.0f && single[3]==0.0f) )
{
xScale = (float)Math.sqrt(Math.pow(single[0], 2)+
Math.pow(single[1], 2));
}
return xScale;
}
(Excerpt from Matrix.java)
While obviously someone did spend some thoughts on this (look at the comment!), the implementation is somewhat inconsistent:
If there are non-zero values in single[1] or single[3], the calculation in the if block results in a non-negative method result.
For zero values in both single[1] and single[3], though, single[0] is returned as-is which may be negative.
A consistent implementation would either always remove the sign or always try to determine a meaningful sign
Furthermore the calculation is somewhat simplistic as it only considers transformation matrices which can be written as product of a scaling and a rotation. These are very common types but by far not all possible ones.
I've created a huge list of colours with names and RGB values (Took a very long time) now I've created an algorithm that gets the corresponding colour to the closest values.
It seems to work very well BUT sometimes when there's an odd value that's completely out it gets the wrong colour.
Example output
Log: InputRGB: R:7.1009636 | G:83.84344 | B:2.5013387
Log: ColorToCompare: Ball Blue (R13.0,G67.0,B80.0) CLOSE:0.4588677 | CurrentColor: Acid Green CLOSE: 0.41585693
Log: ColorToCompare: Bitter Lemon (R79.0,G88.0,B5.0) CLOSE:0.5143066 | CurrentColor: Ball Blue CLOSE: 0.4588677
Log: ColorToCompare: Citrine (R89.0,G82.0,B4.0) CLOSE:0.5610447 | CurrentColor: Bitter Lemon CLOSE: 0.5143066
Log: ColorToCompare: Smoky Black (R6.0,G5.0,B3.0) CLOSE:0.57945675 | CurrentColor: Citrine CLOSE: 0.5610447
Log: ColorName:Smoky Black
Log: End Color: R:6.0 G:5.0 B:3.0
Log: InputRGB: R:7.1009636 | G:83.84344 | B:2.5013387
The code I've created to calculate this:
public String getClosetColor(float red, float green, float blue){
Functions.log("InputRGB: R:" + red + " | G:" + green + " | B:" + blue);
Color lastColor = null;
for(Color eachColor : this.colors)
{
if(lastColor == null){
lastColor = eachColor;
}
float lastColorCloseness = (getClose(red, lastColor.red) + getClose(green, lastColor.green) + getClose(blue, lastColor.blue)) / 3f;
float thisColorCloseness = (getClose(red, eachColor.red) + getClose(green, eachColor.green) + getClose(blue, eachColor.blue)) / 3f;
if(Float.isFinite(thisColorCloseness) && Float.isFinite(lastColorCloseness))
{
//If they are the same, choose a random one.
if(lastColorCloseness == thisColorCloseness){
if(MathUtils.random() > 0.5f){
lastColor = eachColor;
}
}
//If this one is greater then set it.
else if(thisColorCloseness > lastColorCloseness){
Functions.log(
"ColorToCompare: " + eachColor.nameOfColor + " (R" + eachColor.red + ",G" + eachColor.green + ",B" + eachColor.blue + ") CLOSE:" + thisColorCloseness +
" | CurrentColor: " + lastColor.nameOfColor + " CLOSE: " + lastColorCloseness
);
lastColor = eachColor;
}
}
}
Functions.log("ColorName:" + lastColor.nameOfColor);
Functions.log("End Color: R:" + lastColor.red + " G:" + lastColor.green + " B:" + lastColor.blue);
Functions.log("InputRGB: R:" + red + " | G:" + green + " | B:" + blue);
return "";
}
//Basically if one is higher than the other then devide by it.
private float getClose(float firstNumber, float secondNumber){
if(firstNumber < secondNumber){
return firstNumber / secondNumber;
}
else{
return secondNumber / firstNumber;
}
}
I don't know how you came up with your distance function but it's a bit awkward. Let me explain:
You use the ratio of colors instead of the difference like:
float lastColorCloseness = (getClose(red, lastColor.red) + getClose(green, lastColor.green) + getClose(blue, lastColor.blue)) / 3f;
This has the strange effect of not applying equally to equally distanced colors. For example compare
col1(100, 50, 200) with col2(50, 100, 150) and col3(150, 100, 250).
Well, assuming that col2 and col3 have distance from col1 equals:
abs(100-50)+abs(50-100)+abs(200-150)=150
abs(100-150)+abs(50-100)+abs(200-250)=150
your distance function is giving different results:
(50/100+50/100+150/250)/3=0.53
(50/100+50/100+200/250)/3=0.6
And as #David Wallace mentioned it's not the most exaggerated results.
Use a distance function like Euclidean instead.
This is happening because your getClose method isn't doing a good job. If two numbers are both very small, the gap between them is greatly exaggerated.
You'd be much better off doing something like
1 / ( 1 + ( firstNumber - secondNumber ) * ( firstNumber - secondNumber ))
in getClose.
For defining a measure of "closeness", you would need to adjust for human eyes color perception plus possibly the display device.
First, a distance of X in one channel is worse than a distance of X/2 in two channels (a change in tone is more apparent than a comparable change in brightness). The larger a single channel distance the less "similar" the color is. Simply adding differences doesn't accout for that.
A simple to implement measure of distance is the difference between channels squared:
int deltaR = red1 - red2;
int deltaG = green1 - green2;
int deltaB = blue1 - blue2;
int distance = (deltaR * deltaR) + (deltaG * deltaG) + (deltaB * deltaB);
The human eye is not equally sensitive to each color channel. So you might want to adjust weighting color channels for that. A simple weighting can be derived from RGB to grayscale weigthing (Converting RGB to grayscale/intensity)
Modifying distance function for the weights gives:
float distance = (deltaR * deltaR) * 0.2989F
+ (deltaG * deltaG) * 0.5870F
+ (deltaB * deltaB) * 0.1140F;
That should provide a reasonable closeness function. Simply chose the color with smallest color distance.
I need the size of the black part of this image:
I've done some research about how to find it in normal math, and I was pointed to this website: Website
The final answer on getting it was
(from MathWorld - A Wolfram Web Resource: wolfram.com)
where r is the radius of the first circle, R the radius of the second circle, and d the distance between the two centers.
The code I tried to use to get the size of this was the following:
float r = getRadius1();
float R = e.getRadius1();
float deltaX = Math.abs((getX() + getRadius()) - (e.getX() + e.getRadius()));
float deltaY = Math.abs((getY() + getRadius()) - (e.getY() + e.getRadius()));
float d = (float) Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
float part, part2, part3;
//Chopping it in parts, because it's easier.
part = (float) (Math.pow(r,2) * Math.acos(
Math.toRadians((Math.pow(d, 2) + Math.pow(r, 2) - Math.pow(R, 2))/(2*d*r))));
part2 = (float) (Math.pow(R,2) * Math.acos(
Math.toRadians((Math.pow(d, 2) + Math.pow(R, 2) - Math.pow(r, 2))/(2*d*R))));
part3 = (float) (0.5 * Math.sqrt((-d + r + R) * (d+r-R) * (d-r+R) * (d+r+R)));
float res = part + part2 - part3;
Main.log(res + " " + part + " " + part2 + " " + part3+ " "
+ r + " " + R + " " + d);
//logs the data and System.out's it
I did some testing, and the output was this:
1345.9663 621.6233 971.1231 246.78008 20.0 25.0 43.528286
So that indicates that the size of the overlapping part was bigger than the circle itself (which is r^2 * PI).
What did I do wrong?
Just a guess (as stated in my comment): try removing the Math.toRadians(...) conversion.
Since there are no degrees involved in the formula but rather radii, I assume the parameter to cos-1(...) is already a value in radians.
If I remove the conversion and run your code, I get the following overlap area size: 11.163887023925781 which seems plausible since the length of the overlap segment on the line between the two centers is 20 + 25 - 43.5 = 1.5 (approximated)
Edit:
If I set the distance to 5 (the smaller circle is completely contained in the bigger one but touches its edge) I get the overlap area size 1256.63 which is exactly the area of the smaller circle (202 * Π). The calculation doesn't seem to work if the distance is smaller than the difference of the radii (i.e. in your case smaller than 5), but that might just be a problem of numerical representation (the normal datatypes might not be able to represent some of the intermediate results).