How to draw scout/reference lines in dicom - c#

I am a beginner to dicom development group . I need to create a localizer image line on dicom image . So, is there any good ideas . Any Geeks .

David Brabant put you already in the right direction (if you want to work with DICOM you should definitely read and treasure dclunie's medical image FAQ). Let's see if I can elaborate on it and make it easier for you to implement.
I assume you have a tool/library to extract tags from a DICOM file (Offis' DCMTK?). For the sake of exemplification I'll refer to a CT scan (many slices, i.e. many images) and a scout image, onto which you want to display localizer lines. Each DICOM image, including your CT slices and your scout, contain full information about their location in space, in these two tags:
Group,Elem VR Value Name of the tag
---------------------------------------------------------------------
(0020,0032) DS [-249.51172\-417.51172\-821] # ImagePositionPatient
X0 Y0 Z0
(0020,0037) DS [1\0\0\0\1\0] # ImageOrientationPatient
A B C D E F
ImagePositionPatient has the global coordinates in mm of the first pixel transmitted (the top left-hand corner pixel, to be clear) expressed as (x,y,z). I marked them X0, Y0, Z0. ImageOrientationPatient contains two vectors, both of three components, specifying the direction cosines of the first row of pixels and first column of pixels of the image. Understanding direction cosines doesn't hurt (see e.g. http://mathworld.wolfram.com/DirectionCosine.html), but the method suggested by dclunie works directly with them, so for now let's just say they give you the orientation in space of the image plane. I marked them A-F to make formulas easier.
Now, in the code given by dclunie (I believe it's intended to be C, but it's so simple it should work as well as Java, C#, awk, Vala, Octave, etc.) the conventions are the following:
scr_* = refers to the soruce image, i.e. the CT slice
dst_* = refers to the destination image, i.e. the scout
*_pos_x, *_pos_y, *_pos_z = the X0, Y0, Z0 above
*_row_dircos_x, *_row_dircos_y, *_row_dircos_z = the A, B, C above
*_col_dircos_x, *_col_dircos_y, *_col_dircos_z = the D, E, F above
After setting the right values just apply these:
dst_nrm_dircos_x = dst_row_dircos_y * dst_col_dircos_z
- dst_row_dircos_z * dst_col_dircos_y;
dst_nrm_dircos_y = dst_row_dircos_z * dst_col_dircos_x
- dst_row_dircos_x * dst_col_dircos_z;
dst_nrm_dircos_z = dst_row_dircos_x * dst_col_dircos_y
- dst_row_dircos_y * dst_col_dircos_x;
src_pos_x -= dst_pos_x;
src_pos_y -= dst_pos_y;
src_pos_z -= dst_pos_z;
dst_pos_x = dst_row_dircos_x * src_pos_x
+ dst_row_dircos_y * src_pos_y
+ dst_row_dircos_z * src_pos_z;
dst_pos_y = dst_col_dircos_x * src_pos_x
+ dst_col_dircos_y * src_pos_y
+ dst_col_dircos_z * src_pos_z;
dst_pos_z = dst_nrm_dircos_x * src_pos_x
+ dst_nrm_dircos_y * src_pos_y
+ dst_nrm_dircos_z * src_pos_z;
Or, if you have some fancy matrix class, you can build this matrix and multiply it with your point coordinates.
[ dst_row_dircos_x dst_row_dircos_y dst_row_dircos_z -dst_pos_x ]
M = [ dst_col_dircos_x dst_col_dircos_y dst_col_dircos_z -dst_pos_y ]
[ dst_nrm_dircos_x dst_nrm_dircos_y dst_nrm_dircos_z -dst_pos_z ]
[ 0 0 0 1 ]
That would be like this:
Scout_Point(x,y,z,1) = M * CT_Point(x,y,z,1)
Said all that, which points of the CT should we convert to create a line on the scout? Also for this dclunie already suggests a general solution:
"My approach is to project the square that is the bounding box of the source image (i.e. lines joining the TLHC, TRHC,BRHC and BLHC of the slice)."
If you project the four corner points of the CT slice, you'll have a line for CT slices perpendicular to the scout, and a trapezoid in case of non perpendicular slices. Now, if your CT slice is aligned with the coordinate axes (i.e. ImageOrientationPatient = [1\0\0\0\1\0]), the four points are trivial. You compute the width/height of the image in mm using the number of rows/columns and the pixel distance along x/y direction and sum things up appropriately. If you want to implement the generic case, then you need a little trigonometry... or maybe not. It's maybe time you read the definition of the direction cosines if you haven't yet.
I'll try to put you on track. E.g. working on the TRHC, you know where the voxel is in the image plane:
# Pixel location of the TRHC
x_pixel = number_of_columns-1 # Counting from 0
y_pixel = 0
z_pixel = 0 # We're on a plane!
The pixel distance values in DICOM are referred to the image plane, so you can simply multiply x and y by those values to have their position in mm, while z is 0 (both pixels and mm). I am talking about these values:
(0028,0011) US 512 # 2, 1 Columns
(0028,0010) US 512 # 2, 1 Rows
(0028,0030) DS [0.9765625\0.9765625] # 20, 2 PixelSpacing
The matrix M above, is a generic transformation from global to image coordinates, having the direction cosines available. What you need now is something that does the inverse job (image to global) and on the source images (the CT slices). I'll let you go and dig in the geometry books to be sure, but I think it should be something like this (the rotation part is transposed, translation has no sign change and of course we use the src_* values):
[src_row_dircos_x src_col_dircos_x src_nrm_dircos_x src_pos_x ]
M2 = [src_row_dircos_y src_col_dircos_y src_nrm_dircos_y src_pos_y ]
[src_row_dircos_z src_col_dircos_z src_nrm_dircos_z src_pos_z ]
[0 0 0 1 ]
Convert points in the CT slice (e.g. the four corners) to millimeters and then apply M2 to have them in global coordinates. Then you can feed them to the procedure reported by dclunie. Cross-check my maths before using it e.g. for patient diagnostics! ;-)
Hope this helps understanding better dclunie's method. Cheers

Related

Rectangle packing algorithm with desired position?

I'd like to implement a variation of a rectangle packing algorithm in C#. In my case the rectangles have a width and height and a "desired" position in a 2D plane (on the screen). They must however not overlap. I want the algorithm to find the positions of the rectangles that minimizes the distances of their desired positions. I am aware that the order in which the rectangles are placed plays a role but I can't even find a performant algorithm for a fixed or random order. Anyone got an idea or references?
More formal definiton of the problem here
I implemented #tiliavirga's suggestion and it works quite well.
Some notes:
I made the repulsive force proportional to only the square root of the overlapping area because otherwise, the first few iterations had huge repulsive forces blowing the constellation apart. (On the other hand, it leads to quick termination which could be important, see below)
I reduced the attractive force over time towards 0, because otherwise, the alg oscillates in some cases, where overlapping rectangles are pushed away, then in the next iteration pulled together, then pushed away, and so on
The algorithm can take very long, depending on the parameters (1) how quickly the attractive force weakens, (2) how large the motion of the rectangles is in each iteration, and (3) the limit of the total overlapping area which can be tolerated, terminating the algorithm. In time-critical applications, e.g. in games where this computation is done every frame, these parameters should be adjusted to result in a quick termination with a not-so-optimal solution.
All in all, a good enough solution for me. Python code below:
DATA STRUCTURE:
class Rect(object):
def __init__(self, centerX, centerY, width, height):
self.centerX = centerX
self.centerY = centerY
self.desired_centerX = centerX
self.desired_centerY = centerY
self.left = centerX - width / 2
self.right = centerX + width / 2
self.bottom = centerY - height / 2
self.top = centerY + height / 2
self.width = width
self.height = height
def move(self, x, y):
self.centerX += x
self.centerY += y
self.left += x
self.right += x
self.bottom += y
self.top += y
UTILITY:
def normalize(vector):
length = np.linalg.norm(vector)
# define the normalization of the zero vector like this, because we need to move rectangles
# somewhere when they are perfectly centered on each other
if length == 0:
return np.random.rand(vector.shape[0])
else:
return vector / length
def isOverlapping(r1, r2):
#we define that a rects doesn't overlap with itself
if r1 is r2:
return False
if r1.left > r2.right or r1.right < r2.left or r1.bottom > r2.top or r1.top < r2.bottom:
return False
return True
def getOverlappingArea(r1, r2):
if not isOverlapping(r1, r2):
return 0
else:
return (min(r1.right, r2.right) - max(r1.left, r2.left)) * \
(min(r1.right, r2.right) - max(r1.left, r2.left))
#pointing from "r1" to "r2"
def getScaledPushingForce(r1, r2):
overlappingArea = getOverlappingArea(r1, r2)
if overlappingArea < 0:
raise ValueError("Something went wrong, negative overlapping area calculated!")
if overlappingArea == 0:
return np.array([0,0])
return np.sqrt(overlappingArea) * normalize( \
np.array([r2.centerX - r1.centerX, r2.centerY - r1.centerY]))
PARAMETERS:
# the strength of the pulling force towards the desired position decays to easy termination
# higher value = slower decay
# faster decay means faster termination but worse results
pullingForceHalfTime = 10
# the overlapping area which is considered to be small enough to stop the algorithm
# (recommended to assign according to the number and size of the rectangles)
acceptableOverlap = 2*len(rects)
# the scaling of the total forces, that moves the rectangles
# larger portions mean faster termination but possibly worse results
# (recommended 1/2<= forceScaling <= 1/20, the smaller pullingStrength is, the lower should forceScaling also be
# e.g. forceScaling = 1/20 * pullingStrength)
forceScaling = 1/10
ALGORITHM:
# calculates pulling and pushing forces and moves the rectangles a bit in the direction of the combination of these forces
# in every iteration. Stops when the overlapping area is sufficiently small
def unstack():
i = 1
#iterate until break
while True:
#pulling forces towards the desired position
#weakened over the course of the iteration (depending on d), since no overlapping is the stronger constraint
pulling_forces = [np.array([r.desired_centerX - r.centerX, r.desired_centerY - r.centerY]) * \
np.power(0.5, i/pullingForceHalfTime) for r in rects]
#pushing forces resulting from overlapping rectangles
#the directions of the forces for a pair of overlapping rectangles has the direction of the connecting vector
#between their centers and the magnitude is proportional to the are of the overlap
pushing_forces = [np.sum([getScaledPushingForce(r_, r) for r_ in rects], axis=0) for r in rects]
total_forces = np.sum([pulling_forces, pushing_forces], axis=0) * forceScaling
#move the rectangles by a portion of the total forces (smaller steps => more iterations but better results)
for j in range(len(rects)):
rects[j].move(total_forces[j][0], total_forces[j][1])
#stop iterating when the total overlapping area is sufficiently small
if np.sum(np.square([getOverlappingArea(r[0], r[1]) for r in itertools.combinations(rects, 2)])) <= acceptableOverlap:
break
i += 1
#print results
finalDistancesFromDesired = [np.array([r.desired_centerX - r.centerX, r.desired_centerY - r.centerY]) for r in rects]
print("Total distances to desired positions: " + str(np.sum(np.linalg.norm(finalDistancesFromDesired, axis = 1))))
and an example run through:
Example

Space represented by a single Kinect pixel at a given depth

Basically I want to take a fixed straight line across the devices point of view and determine if anything intercepts it but in my example I want to make the "laser line" configurable with regards to the distance from the top of the field of view.
Now it's easy enough to get the depth data at a given pixel point simply by doing this.
var depthInMM = DepthImagePixel.Depth;
and its also easy to simply say I want to focus on the 100th line of pixels from the top by doing something like this.
for (int i = 0; i < this._DepthPixels.Length; ++i) //_DepthPixels.Length is obviously 307200 for 640x480
{
if (i >= 64000 && i <= 64640) //Hundredth vertical pixel line
{
//Draw line or whatever
}
}
Which ends up with something like this.
BUT for example I might want to have the line intercept at 50 cm from the top of the field of view at 3 meters depth. Now obviously I understand that as the depth increases so does the area represented but I cannot find any reference or myself work out how to calculate this relationship.
So, how can one calculate the coordinate space represented at a given depth utilizing the Kinect sensor. Any help sincerely appreciated.
EDIT:
So if I understand correctly this can be implemented as such in C#
double d = 2; //2 meters depth
double y = 100; //100 pixels from top
double vres = 480; //480 pixels vertical resolution
double vfov = 43; //43 degrees vertical field of view of Kinect
double x = (2 * Math.Sin(Math.PI * vfov / 360) * d * y) / vres;
//x = 0.30541768893691434
//x = 100 pixels down is 30.5 cm from top field of view at 2 meters depth
2 sin(PI VFOV / 360) D Y
X = --------------------------
VRES
X: distance of your line from the top of the image in meters
D: distance - orthogonal to the image plane - of your line from the camera in meters
Y: distance of your line from the top of the image in pixels
VRES: vertical resolution of the image in pixels
VFOV: vertical field of view of the camera in degrees

Calculating coordinates in circle

My problem basically is to calculate the x and y coordinates of the second element in the following situation.
Is for a tool in unity3d, using c#
So I'm guessing you have the coordinates of A and B. Find the angle of the second line with:
float angle = atan2(B.y-A.y, B.x-A.x)
This only works if your situation is axis-aligned like in your diagram (i.e. if the original configuration is lined up along the x axis). If not, you can solve the formula |U x V| = |U| |V| sin(angle) for angle (you will need an arcsin -- the inverse of sin), where U and V are the old and new AB vectors.
Then rotate your point of interest (call it P) around A. You do this by first subtracting A's coordinates from P so the axis of rotation is at the origin. Then rotate P by multiplying with the rotation matrix:
[ cos(angle) -sin(angle) ] [ P.x ]
[ sin(angle) cos(angle) ] [ P.y ]
Which gives
x = cos(angle) * P.x - sin(angle) * P.y
y = sin(angle) * P.x + cos(angle) * P.y
After you have these, add A's coordinates back in.
In summary:
P_new = A + rotate(P_old - A)
The actual code will be more involved than this, but this is the abstract picture. I'll leave the coding to you.
If you know the angle between the x-axis and this box shaped object you can use some basic trig.
Lets assume the box shaped object has a length of "len". ( It would really help to better label what is going on, I don't fully understand.) and lets say you have an angle of θ.
I'm assuming the centre of the circle has coordinates (0,0).
The then the vertical distance from the centre of the circle to the edge of the circle where the box touches is y = len * sin(θ) and for the horizontal distance is len * cos(θ).
If you are only going about 2/5ths of the way up the box thing you would use len / 5 instead of len.
This is just the math behind it. In c# you want to use the Math class. it has all the functions you need. Be careful with radians and degrees.

Relationship between projected and unprojected Z-Values in Direct3D

I've been trying to figure this relationship out but I can't, maybe I'm just not searching for the right thing. If I project a world-space coordinate to clip space using Vector3.Project, the X and Y coordinates make sense but I can't figure out how it's computing the Z (0..1) coordinate. For instance, if my nearplane is 1 and farplane is 1000, I project a Vector3 of (0,0,500) (camera center, 50% of distance to far plane) to screen space I get (1050, 500, .9994785)
The resulting X and Y coordinates make perfect sense but I don't understand where it's getting the resulting Z-value.
I need this because I'm actually trying to UNPROJECT screen-space coordinates and I need to be able to pick a Z-value to tell it the distance from the camera I want the world-space coordinate to be, but I don't understand the relationship between clip space Z (0-1) and world-space Z (nearplane-farplane).
In case this helps, my transformation matrices are:
World = Matrix.Identity;
//basically centered at 0,0,0 looking into the screen
View = Matrix.LookAtLH(
new Vector3(0,0,0), //camera position
new Vector3(0,0,1), //look target
new Vector3(0,1,0)); //up vector
Projection = Matrix.PerspectiveFovLH(
(float)(Math.PI / 4), //FieldOfViewY
1.6f, // AspectRatio
1, //NearPlane
1000); //FarPlane
Standard perspective projection creates a reciprocal relationship between the scene depth and the depth buffer value, not a linear one. This causes a higher percentage of buffer precision to be applied to objects closer to the near plane than those closer to the far plane, which is typically desired. As for the actual math, here's the breakdown:
The bottom-right 2x2 elements (corresponding to z and w) of the projection matrix are:
[far / (far - near) ] [1]
[-far * near / (far - near)] [0]
This means that after multiplying, z' = z * far / (far - near) - far * near / (far - near) and w' = z. After this step, there is the perspective divide, z'' = z' / w'.
In your specific case, the math works out to the value you got:
z = 500
z' = z * 1000 / (1000 - 999) - 1000 / (1000 - 999) = 499.499499499...
w' = z = 500
z'' = z' / w' = 0.998998998...
To recover the original depth, simply reverse the operations:
z = (far / (far - near)) / ((far / (far - near)) - z'')

Logarithmic Spiral - Is Point on Spiral (cartesian coordinates

Lets Say I have a 3d Cartesian grid. Lets also assume that there are one or more log spirals emanating from the origin on the horizontal plane.
If I then have a point in the grid I want to test if that point is in one of the spirals. I acutally want to test if it within a certain range of the spirals but determining if it is on the point is a good start.
So I guess the question has a couple parts.
How to generate the arms from parameters (direction, tightness)
How to tell if a point in the grid is in one of the spiral arms
Any ideas? I have been googling all day and don't feel I am any closer to a solution than when I started.
Here is a bit more information that might help:
I don't actually need to render the spirals. I want to set the pitch and rotation and then pass a point to a method that can tell me if the point I passed is within the spiral (within a given range of any point on the spiral). Based on the value returned (true or false) my program will make a decision on whether or not something exists at the point in space.
How to parametrically define the log spirals (pitch and rotation and ??)
Test if a point (x, y, z) is withing a given range of any point on the spiral.
Note: Both of the above would be just on the horizontal plane
These are two functions defining an anti-clockwise spiral:
PolarPlot[{
Exp[(t + 10)/100],
Exp[t/100]},
{t, 0, 100 Pi}]
Output:
These are two functions defining a clockwise spiral:
PolarPlot[{
- Exp[(t + 10)/100],
- Exp[t/100]},
{t, 0, 100 Pi}]
Output:
Cartesian coordinates
The conversion Cartesian <-> Polar is
(1) Ro = Sqrt[x^2+y^2]
t = ArcTan[y/x]
(2) x = Ro Cos[t]
y = Ro Sin[t]
So, If you have a point in Cartesian Coords (x,y) you transform it to your equivalent polar coordinates using (1). Then you use the forula for the spiral function (any of the four mentinoned above the plots, or similar ones) putting in there the value for t, and obtaining Ro. The last step is to compare this Ro with the one we got from the coordinates converion. If they are equal, the point is on the spiral.
Edit Answering your comment
For a Log spiral is almost the same, but with multiple spirals you need to take care of the logs not going to negative values. That's why I used exponentials ...
Example:
PolarPlot[{
Log[t],
If[t > 3, Log[ t - 2], 0],
If[t > 5, Log[ t - 4], 0]
}, {t, 1, 10}]
Output:
Not sure this is what you want, but you can reverse the log function (or "any" other for that matter).
Say you have ln A = B, to get A from B you do e^B = A.
So you get your point and pass it as B, you'll get A. Then you just need to check if that A (with a certain +- range) is in the values you first passed on to ln to generate the spiral.
I think this might work...
Unfortunately, you will need to know some mathematics notation anyway - this is a good read about the logarithmic sprial.
http://en.wikipedia.org/wiki/Logarithmic_spiral
we will only need the top 4 equations.
For your question 1
- to control the tightness, you tune the parameter 'a' as in the wiki page.
- to control the direction, you offset theta by a certain amount.
For your question 2
In floating point arithmetic, you will never get absolute precision, which mean there will be no point falling exactly on the sprial. On the screen, however, you will know which pixel get rendered, and you can test whether you are hitting a point that is rendered.
To render a curve, you usually render it as a sequence of line segments, short enough so that overall it looks like a curve. If you want to know whether a point lies within certain distance from the spiral, you can render the curve (on a off-screen buffer if you wish) by having thicker lines.
here a C++ code drawing any spiral passing where the mouse here
(sorry for my English)
int cx = pWin->vue.right / 2;
int cy = pWin->vue.bottom / 2;
double theta_mouse = atan2((double)(pWin->y_mouse - cy),(double)(pWin->x_mouse - cx));
double square_d_mouse = (double)(pWin->y_mouse - cy)*(double)(pWin->y_mouse - cy)+
(double)(pWin->x_mouse - cx)*(double)(pWin->x_mouse - cx);
double d_mouse = sqrt(square_d_mouse);
double theta_t = log( d_mouse / 3.0 ) / log( 1.19 );
int x = cx + (3 * cos(theta_mouse));
int y = cy + (3 * sin(theta_mouse));
MoveToEx(hdc,x,y,NULL);
for(double theta=0.0;theta < PI2*5.0;theta+=0.1)
{
double d = pow( 1.19 , theta ) * 3.0;
x = cx + (d * cos(theta-theta_t+theta_mouse));
y = cy + (d * sin(theta-theta_t+theta_mouse));
LineTo(hdc,x,y);
}
Ok now the parameter of spiral is 1.19 (slope) and 3.0 (radius at center)
Just compare the points where theta is a mutiple of 2 PI = PI2 = 6,283185307179586476925286766559
if any points is near of a non rotated spiral like
x = cx + (d * cos(theta));
y = cy + (d * sin(theta));
then your mouse is ON the spiral... I searched this tonight and i googled your past question

Categories

Resources