Skip to content

Commit

Permalink
Go back and forth from VolumeData to OSIROIMask
Browse files Browse the repository at this point in the history
  • Loading branch information
spalte committed Feb 15, 2015
1 parent 7702254 commit 4f6f76d
Show file tree
Hide file tree
Showing 5 changed files with 592 additions and 78 deletions.
11 changes: 11 additions & 0 deletions OsiriXClasses/CPR/CPRVolumeData.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ typedef struct { // build one of these on the stack and then use -[CPRVolumeData
- (CPRUnsignedInt16ImageRep *)unsignedInt16ImageRepForSliceAtIndex:(NSUInteger)z;
- (CPRVolumeData *)volumeDataForSliceAtIndex:(NSUInteger)z;

- (CPRVolumeData *)volumeDataByApplyingTransform:(N3AffineTransform)transform;

// the first version of this function figures out the dimensions needed to fit the whole volume. Note that with the first version of this function the passed in transform may not
// be equal to the volumeTransform of the returned volumeData because a minimum cube of data needed to fit the was calculated. Any shift in the data is guaranteed to be a multiple
// of the basis vectors of the transform though.
- (instancetype)volumeDataResampledWithVolumeTransform:(N3AffineTransform)transform interpolationMode:(CPRInterpolationMode)interpolationsMode;
- (instancetype)volumeDataResampledWithVolumeTransform:(N3AffineTransform)transform pixelsWide:(NSUInteger)pixelsWide pixelsHigh:(NSUInteger)pixelsHigh pixelsDeep:(NSUInteger)pixelsDeep
interpolationMode:(CPRInterpolationMode)interpolationsMode;

- (BOOL)getFloat:(float *)floatPtr atPixelCoordinateX:(NSUInteger)x y:(NSUInteger)y z:(NSUInteger)z; // returns YES if the float was sucessfully gotten
- (BOOL)getLinearInterpolatedFloat:(float *)floatPtr atDicomVector:(N3Vector)vector; // these are slower, use the inline buffer if you care about speed
- (BOOL)getNearestNeighborInterpolatedFloat:(float *)floatPtr atDicomVector:(N3Vector)vector; // these are slower, use the inline buffer if you care about speed
Expand All @@ -94,6 +103,8 @@ typedef struct { // build one of these on the stack and then use -[CPRVolumeData
- (void)linearInterpolateVolumeVectors:(N3VectorArray)volumeVectors outputValues:(float *)outputValues numVectors:(NSUInteger)numVectors tempBuffer:(void *)tempBuffer;
// end not done

- (BOOL)isEqual:(id)object;

- (BOOL)isDataValid __deprecated;
- (void)invalidateData __deprecated;
- (void)releaseInlineBuffer:(CPRVolumeDataInlineBuffer *)inlineBuffer __deprecated; // CPRVolumeTransform can no longer be invalided so the this is no longer needed
Expand Down
84 changes: 84 additions & 0 deletions OsiriXClasses/CPR/CPRVolumeData.m
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,66 @@ - (BOOL)getNearestNeighborInterpolatedFloat:(float *)floatPtr atDicomVector:(N3V
return YES;
}

- (instancetype)volumeDataByApplyingTransform:(N3AffineTransform)transform;
{
return [[[CPRVolumeData alloc] initWithData:_floatData pixelsWide:_pixelsWide pixelsHigh:_pixelsHigh pixelsDeep:_pixelsDeep
volumeTransform:N3AffineTransformConcat(_volumeTransform, transform) outOfBoundsValue:_outOfBoundsValue] autorelease];
}

- (instancetype)volumeDataResampledWithVolumeTransform:(N3AffineTransform)transform interpolationMode:(CPRInterpolationMode)interpolationsMode
{
if (N3AffineTransformEqualToTransform(_volumeTransform, transform)) {
return self;
}

N3AffineTransform oringinalVoxelToDicomTransform = N3AffineTransformInvert(_volumeTransform);
N3AffineTransform originalVoxelToNewVoxelTransform = N3AffineTransformConcat(oringinalVoxelToDicomTransform, transform);

N3Vector minCorner = N3VectorZero;
N3Vector maxCorner = N3VectorZero;

N3Vector corner1 = N3VectorApplyTransform(N3VectorMake(0, 0, 0), originalVoxelToNewVoxelTransform);
N3Vector corner2 = N3VectorApplyTransform(N3VectorMake(_pixelsWide, 0, 0), originalVoxelToNewVoxelTransform);
N3Vector corner3 = N3VectorApplyTransform(N3VectorMake(0, _pixelsHigh, 0), originalVoxelToNewVoxelTransform);
N3Vector corner4 = N3VectorApplyTransform(N3VectorMake(_pixelsWide, _pixelsHigh, 0), originalVoxelToNewVoxelTransform);
N3Vector corner5 = N3VectorApplyTransform(N3VectorMake(0, 0, _pixelsDeep), originalVoxelToNewVoxelTransform);
N3Vector corner6 = N3VectorApplyTransform(N3VectorMake(_pixelsWide, 0, _pixelsDeep), originalVoxelToNewVoxelTransform);
N3Vector corner7 = N3VectorApplyTransform(N3VectorMake(0, _pixelsHigh, _pixelsDeep), originalVoxelToNewVoxelTransform);
N3Vector corner8 = N3VectorApplyTransform(N3VectorMake(_pixelsWide, _pixelsHigh, _pixelsDeep), originalVoxelToNewVoxelTransform);

minCorner.x = MIN(MIN(MIN(MIN(MIN(MIN(MIN(corner1.x, corner2.x), corner3.x), corner4.x), corner5.x), corner6.x), corner7.x), corner8.x);
minCorner.y = MIN(MIN(MIN(MIN(MIN(MIN(MIN(corner1.y, corner2.y), corner3.y), corner4.y), corner5.y), corner6.y), corner7.y), corner8.y);
minCorner.z = MIN(MIN(MIN(MIN(MIN(MIN(MIN(corner1.z, corner2.z), corner3.z), corner4.z), corner5.z), corner6.z), corner7.z), corner8.z);
maxCorner.x = MAX(MAX(MAX(MAX(MAX(MAX(MAX(corner1.x, corner2.x), corner3.x), corner4.x), corner5.x), corner6.x), corner7.x), corner8.x);
maxCorner.y = MAX(MAX(MAX(MAX(MAX(MAX(MAX(corner1.y, corner2.y), corner3.y), corner4.y), corner5.y), corner6.y), corner7.y), corner8.y);
maxCorner.z = MAX(MAX(MAX(MAX(MAX(MAX(MAX(corner1.z, corner2.z), corner3.z), corner4.z), corner5.z), corner6.z), corner7.z), corner8.z);

#if CGFLOAT_IS_DOUBLE
minCorner.x = floor(minCorner.x);
minCorner.y = floor(minCorner.y);
minCorner.z = floor(minCorner.z);
maxCorner.x = ceil(maxCorner.x);
maxCorner.y = ceil(maxCorner.y);
maxCorner.z = ceil(maxCorner.z);
#else
minCorner.x = floorf(minCorner.x);
minCorner.y = floorf(minCorner.y);
minCorner.z = floorf(minCorner.z);
maxCorner.x = ceilf(maxCorner.x);
maxCorner.y = ceilf(maxCorner.y);
maxCorner.z = ceilf(maxCorner.z);
#endif

NSUInteger width = (NSUInteger)(maxCorner.x - minCorner.x) + 1;
NSUInteger height = (NSUInteger)(maxCorner.y - minCorner.y) + 1;
NSUInteger depth = (NSUInteger)(maxCorner.z - minCorner.z) + 1;

N3AffineTransform shiftedTransform = N3AffineTransformConcat(transform, N3AffineTransformMakeTranslation(-1.0*(CGFloat)minCorner.x, -1.0*(CGFloat)minCorner.y, -1.0*(CGFloat)minCorner.z));

return [self volumeDataResampledWithVolumeTransform:shiftedTransform pixelsWide:width pixelsHigh:height pixelsDeep:depth interpolationMode:interpolationsMode];
}


- (instancetype)volumeDataResampledWithVolumeTransform:(N3AffineTransform)transform pixelsWide:(NSUInteger)pixelsWide pixelsHigh:(NSUInteger)pixelsHigh pixelsDeep:(NSUInteger)pixelsDeep
interpolationMode:(CPRInterpolationMode)interpolationsMode;
{
Expand Down Expand Up @@ -258,6 +318,30 @@ - (NSUInteger)tempBufferSizeForNumVectors:(NSUInteger)numVectors
return numVectors * sizeof(float) * 11;
}

- (BOOL)isEqual:(id)object
{
BOOL isEqual = NO;
if ([object isKindOfClass:[CPRVolumeData class]]) {
CPRVolumeDataInlineBuffer inlineBuffer1;
CPRVolumeDataInlineBuffer inlineBuffer2;
CPRVolumeData *otherVolumeData = (CPRVolumeData *)object;

[self aquireInlineBuffer:&inlineBuffer1];
[otherVolumeData aquireInlineBuffer:&inlineBuffer2];

if (inlineBuffer1.floatBytes == inlineBuffer2.floatBytes &&
inlineBuffer1.outOfBoundsValue == inlineBuffer2.outOfBoundsValue &&
inlineBuffer1.pixelsWide == inlineBuffer2.pixelsWide &&
inlineBuffer1.pixelsHigh == inlineBuffer2.pixelsHigh &&
inlineBuffer1.pixelsDeep == inlineBuffer2.pixelsDeep &&
N3AffineTransformEqualToTransform(inlineBuffer1.volumeTransform, inlineBuffer2.volumeTransform)) {
isEqual = YES;
}
}

return isEqual;
}

// not done yet, will crash if given vectors that are outside of the volume
- (void)linearInterpolateVolumeVectors:(N3VectorArray)volumeVectors outputValues:(float *)outputValues numVectors:(NSUInteger)numVectors tempBuffer:(void *)tempBuffer
{
Expand Down
17 changes: 10 additions & 7 deletions OsiriXClasses/OSI/OSIFloatVolumeData.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,16 @@ - (BOOL)getLinearInterpolatedFloat:(float *)floatPtr atDicomVector:(N3Vector)vec
- (BOOL)checkDebugROIMask:(OSIROIMask *)roiMask
{
NSValue *maskRunValue;
OSIROIMaskRun maskRun;

for (maskRunValue in [roiMask maskRuns]) {
maskRun = [maskRunValue OSIROIMaskRunValue];

if (maskRun.depthIndex >= _pixelsDeep || maskRun.heightIndex >= _pixelsHigh ||
maskRun.widthRange.location + maskRun.widthRange.length >= _pixelsWide) {
NSData *maskRunData;
OSIROIMaskRun *maskRuns;
NSInteger i;

maskRunData = [roiMask maskRunsData];
maskRuns = (OSIROIMaskRun *)[maskRunData bytes];

for (i = 0; i < [roiMask maskRunCount]; i++) {
if (maskRuns[i].depthIndex >= _pixelsDeep || maskRuns[i].heightIndex >= _pixelsHigh ||
maskRuns[i].widthRange.location + maskRuns[i].widthRange.length >= _pixelsWide) {
return NO;
}
}
Expand Down
98 changes: 83 additions & 15 deletions OsiriXClasses/OSI/OSIROIMask.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#import <Cocoa/Cocoa.h>
#import "N3Geometry.h"
#import "OSIFloatVolumeData.h"

@class OSIFloatVolumeData;

Expand All @@ -31,6 +32,21 @@ typedef struct OSIROIMaskRun OSIROIMaskRun;

extern const OSIROIMaskRun OSIROIMaskRunZero;

/** Returns a newly created OSIROIMaskRun
*/
OSIROIMaskRun OSIROIMaskRunMake(NSRange widthRange, NSUInteger heightIndex, NSUInteger depthIndex, float intensity);

/** Returns the first width index that is under the run.
*/
NSUInteger OSIROIMaskRunFirstWidthIndex(OSIROIMaskRun maskRun);

/** Returns the last width index that is under the run.
*/
NSUInteger OSIROIMaskRunLastWidthIndex(OSIROIMaskRun maskRun);

/** A structure used to describe a single point in a mask.
*/
Expand All @@ -44,6 +60,11 @@ typedef struct OSIROIMaskIndex OSIROIMaskIndex;

CF_EXTERN_C_BEGIN

/** Transforms the maskIndex using the given transform, and returns an N3Vector
*/
N3Vector OSIROIMaskIndexApplyTransform(OSIROIMaskIndex maskIndex, N3AffineTransform transform);

/** Returns YES if the `maskIndex` is withing the `maskRun`
*/
Expand Down Expand Up @@ -92,7 +113,7 @@ CF_EXTERN_C_END
`NSUInteger heightIndex;`
`NSUInteger depthIndex;`
`float intensity;`
`};`
Expand Down Expand Up @@ -122,7 +143,7 @@ CF_EXTERN_C_END
*/


@interface OSIROIMask : NSObject <NSCopying> {
@interface OSIROIMask : NSObject <NSCopying, NSSecureCoding> {
@private
NSData *_maskRunsData;
NSArray *_maskRuns;
Expand All @@ -136,24 +157,50 @@ CF_EXTERN_C_END
@return The newly crated and initialized ROI Mask object.
*/
+ (id)ROIMask;
+ (instancetype)ROIMask;

/** Returns a newly created ROI Mask that has the shape of a sphere with the specified diameter.
@return The newly crated and initialized ROI Mask object.
*/
+ (instancetype)ROIMaskWithSphereDiameter:(NSUInteger)diameter;

/** Returns a newly created ROI Mask that has the shape of a cube with the specified size.
@return The newly crated and initialized ROI Mask object.
*/
+ (instancetype)ROIMaskWithCubeSize:(NSUInteger)size;

/** Returns a newly created ROI Mask that has the shape of a box with the specified sizes.
@return The newly crated and initialized ROI Mask object.
*/
+ (instancetype)ROIMaskWithBoxWidth:(NSUInteger)width height:(NSUInteger)height depth:(NSUInteger)depth;

/** Returns a newly created ROI Mask that has the shape of an elipsoid with the specified sizes.
@return The newly crated and initialized ROI Mask object.
*/
+ (instancetype)ROIMaskWithElipsoidWidth:(NSUInteger)width height:(NSUInteger)height depth:(NSUInteger)depth;

/** Returns a newly created ROI Mask based on the intesities of the floatVolumeData.
The returned mask is a mask on the floatVolumeData with the intensities of the floatVolumeData.
@return The newly crated and initialized ROI Mask object or `nil` if there was a problem initializing the object.
@param floatVolumeData The OSIFloatVolumeData on which to build and base the mask.
@param volumeTransformPtr Returns the transform needed to go from DICOM space to the mask
*/
+ (id)ROIMaskFromVolumeData:(OSIFloatVolumeData *)floatVolumeData;
+ (instancetype)ROIMaskFromVolumeData:(OSIFloatVolumeData *)floatVolumeData volumeTransform:(N3AffineTransformPointer)volumeTransformPtr;
+ (instancetype)ROIMaskFromVolumeData:(OSIFloatVolumeData *)floatVolumeData __deprecated;

/** Initializes and returns a newly created empty ROI Mask.
Creates an empty ROI Mask.
@return The initialized ROI Mask object.
*/
- (id)init;
- (instancetype)init;

// create the thing, maybe we should really be working with C arrays.... or at least give the option
/** Initializes and returns a newly created ROI Mask.
Expand All @@ -163,7 +210,7 @@ CF_EXTERN_C_END
@return The initialized ROI Mask object or `nil` if there was a problem initializing the object.
@param maskRuns An array of OSIROIMaskRun structs in NSValues.
*/
- (id)initWithMaskRuns:(NSArray *)maskRuns;
- (instancetype)initWithMaskRuns:(NSArray *)maskRuns;

/** Initializes and returns a newly created ROI Mask.
Expand All @@ -172,7 +219,7 @@ CF_EXTERN_C_END
@return The initialized ROI Mask object or `nil` if there was a problem initializing the object.
@param maskRunData is the serialized OSIROIMaskRuns.
*/
- (id)initWithMaskRunData:(NSData *)maskRunData;
- (instancetype)initWithMaskRunData:(NSData *)maskRunData;

/** Initializes and returns a newly created ROI Mask.
Expand All @@ -181,7 +228,7 @@ CF_EXTERN_C_END
@return The initialized ROI Mask object or `nil` if there was a problem initializing the object.
@param maskRunData is the serialized OSIROIMaskRuns.
*/
- (id)initWithSortedMaskRunData:(NSData *)maskRunData;
- (instancetype)initWithSortedMaskRunData:(NSData *)maskRunData;

// create the thing, maybe we should really be working with C arrays.... or at least give the option
/** Initializes and returns a newly created ROI Mask.
Expand All @@ -191,7 +238,7 @@ CF_EXTERN_C_END
@return The initialized ROI Mask object or `nil` if there was a problem initializing the object.
@param maskRuns An array of OSIROIMaskRun structs in NSValues.
*/
- (id)initWithSortedMaskRuns:(NSArray *)maskRuns;
- (instancetype)initWithSortedMaskRuns:(NSArray *)maskRuns;

/** Initializes and returns a newly created ROI Mask.
Expand All @@ -200,7 +247,7 @@ CF_EXTERN_C_END
@return The initialized ROI Mask object or `nil` if there was a problem initializing the object.
@param maskRuns An array of OSIROIMaskIndex structs in NSValues.
*/
- (id)initWithIndexes:(NSArray *)maskIndexes;
- (instancetype)initWithIndexes:(NSArray *)maskIndexes;

/** Initializes and returns a newly created ROI Mask.
Expand All @@ -209,7 +256,7 @@ CF_EXTERN_C_END
@return The initialized ROI Mask object or `nil` if there was a problem initializing the object.
@param indexData is the serialized OSIROIMaskIndexes.
*/
- (id)initWithIndexData:(NSData *)indexData;
- (instancetype)initWithIndexData:(NSData *)indexData;

/** Initializes and returns a newly created ROI Mask.
Expand All @@ -218,7 +265,7 @@ CF_EXTERN_C_END
@return The initialized ROI Mask object or `nil` if there was a problem initializing the object.
@param maskRuns An array of OSIROIMaskIndex structs in NSValues.
*/
- (id)initWithSortedIndexes:(NSArray *)maskIndexes;
- (instancetype)initWithSortedIndexes:(NSArray *)maskIndexes;

/** Initializes and returns a newly created ROI Mask.
Expand All @@ -227,7 +274,7 @@ CF_EXTERN_C_END
@return The initialized ROI Mask object or `nil` if there was a problem initializing the object.
@param maskRuns An array of OSIROIMaskIndex structs in NSValues.
*/
- (id)initWithSortedIndexData:(NSData *)indexData;
- (instancetype)initWithSortedIndexData:(NSData *)indexData;

///-----------------------------------
/// @name Working with the Mask
Expand All @@ -249,10 +296,20 @@ CF_EXTERN_C_END
- (OSIROIMask *)ROIMaskByUnioningWithMask:(OSIROIMask *)otherMask;

/** Returns a mask formed by subtracting otherMask from the receiver.
*/
- (OSIROIMask *)ROIMaskBySubtractingMask:(OSIROIMask *)otherMask;

/** Returns a OSIFloatVolumeData filled with the intensities of the mask.
*/
- (OSIFloatVolumeData *)floatVolumeDataRepresentationWithVolumeTransform:(N3AffineTransform)volumeTransform;

/** Returns a mask formed by cropping any indexes that are further out than the bounds specified from the receiver.
*/
- (OSIROIMask *)ROIMaskCroppedToWidth:(NSUInteger)width height:(NSUInteger)height depth:(NSUInteger)depth;

/** Returns YES if the two masks intersect.
*/
Expand Down Expand Up @@ -312,7 +369,18 @@ CF_EXTERN_C_END
@param index OSIROIMaskIndex struct to test.
*/
- (BOOL)indexInMask:(OSIROIMaskIndex)index;
- (BOOL)containsIndex:(OSIROIMaskIndex)index;
- (BOOL)indexInMask:(OSIROIMaskIndex)index __deprecated;

/** Returns a mask that has been resampled from being in the volume as the position fromTransform to a mask that is in the volume at position toTransform.
*/
- (instancetype)ROIMaskByResamplingFromVolumeTransform:(N3AffineTransform)fromTransform toVolumeTransform:(N3AffineTransform)toTransform interpolationMode:(CPRInterpolationMode)interpolationsMode;

/** Returns the extent of the receiver. All values are inclusive.
*/
- (void)extentMinWidth:(NSUInteger*)minWidthPtr maxWidth:(NSUInteger*)maxWidthPtr minHeight:(NSUInteger*)minHeightPtr maxHeight:(NSUInteger*)maxHeightPtr minDepth:(NSUInteger*)minDepthPtr maxDepth:(NSUInteger*)maxDepthPtr;

/** Returns an array of points that represent the outside bounds of the mask.
Expand Down
Loading

0 comments on commit 4f6f76d

Please sign in to comment.