From 9ba9ad49e80c8e13ecf202cf1f6fdd7f118de1d2 Mon Sep 17 00:00:00 2001 From: Raul Catena Date: Sun, 12 Apr 2020 09:37:19 +0200 Subject: [PATCH] Export masks for registration in python --- 3DIMC.xcodeproj/project.pbxproj | 5 ++++ 3DIMC/IMCComputationOnMask.h | 1 + 3DIMC/IMCComputationOnMask.m | 45 +++++++++++++++++++++++++++++++++ 3DIMC/IMCFileExporter.h | 4 +++ 3DIMC/IMCFileExporter.m | 19 +++++++++++--- 3DIMC/IMCFileWrapper.m | 9 +++++-- 3DIMC/IMCImageGenerator.m | 2 +- 3DIMC/IMCPixelClassification.m | 4 +++ 3DIMC/IMCWorkSpace.mm | 34 +++++++++++++++++++++++++ 3DIMC/IMCWorkspaceSelector.m | 2 ++ histoCAT++3D.entitlements | 5 ++++ 11 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 histoCAT++3D.entitlements diff --git a/3DIMC.xcodeproj/project.pbxproj b/3DIMC.xcodeproj/project.pbxproj index 4089708..09f4649 100644 --- a/3DIMC.xcodeproj/project.pbxproj +++ b/3DIMC.xcodeproj/project.pbxproj @@ -775,6 +775,7 @@ 0D6720511E3FF3C800C19663 /* IMCTransformDictController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IMCTransformDictController.h; sourceTree = ""; }; 0D6720521E3FF3C800C19663 /* IMCTransformDictController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IMCTransformDictController.m; sourceTree = ""; }; 0D6720541E3FF3E400C19663 /* IMCTransformDictController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = IMCTransformDictController.xib; sourceTree = ""; }; + 0D6AFD87244288E700B9BF38 /* histoCAT++3D.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "histoCAT++3D.entitlements"; sourceTree = ""; }; 0D6F4F2A1E473CC200533127 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 0D7CB26B217755F200EA36A5 /* License-AGPL3.0.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "License-AGPL3.0.txt"; sourceTree = ""; }; 0D7CB26F217756EB00EA36A5 /* SOFTWARE CONTRIBUTION AGREEMENT-UZH.doc */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SOFTWARE CONTRIBUTION AGREEMENT-UZH.doc"; sourceTree = ""; }; @@ -1524,6 +1525,7 @@ 0DB37CD21E304D25002975E2 = { isa = PBXGroup; children = ( + 0D6AFD87244288E700B9BF38 /* histoCAT++3D.entitlements */, 0D7CB26F217756EB00EA36A5 /* SOFTWARE CONTRIBUTION AGREEMENT-UZH.doc */, 0D7CB26B217755F200EA36A5 /* License-AGPL3.0.txt */, 0D57600E1EB9CAC2008CAB94 /* Frameworks */, @@ -1933,6 +1935,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -2762,6 +2765,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = "histoCAT++3D.entitlements"; CODE_SIGN_IDENTITY = ""; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = HA3BXXH796; @@ -2825,6 +2829,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = "histoCAT++3D.entitlements"; CODE_SIGN_IDENTITY = ""; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = HA3BXXH796; diff --git a/3DIMC/IMCComputationOnMask.h b/3DIMC/IMCComputationOnMask.h index eb941b0..42c0c1e 100644 --- a/3DIMC/IMCComputationOnMask.h +++ b/3DIMC/IMCComputationOnMask.h @@ -28,6 +28,7 @@ -(void)addFeaturesFromCellProfiler:(NSURL *)url; -(void)extractDataForMask:(NSIndexSet *)computations processedData:(BOOL)rawOrProcessedData; -(float *)createImageForMaskWithCellData:(float *)data maskOption:(MaskOption)option maskType:(MaskType)maskType maskSingleColor:(NSColor *)maskSingleColor; +-(UInt8 *)createImageForCategoricalMaskWithCellDataIndex:(NSUInteger)index maskType:(MaskType)maskType; -(CGImageRef)coloredMaskForChannel:(NSInteger)channel color:(NSColor *)color maskOption:(MaskOption)maskOption maskType:(MaskType)maskType maskSingleColor:(NSColor *)maskSingleColor brightField:(BOOL)brightField; -(BOOL)hasBackData; -(void)saveData; diff --git a/3DIMC/IMCComputationOnMask.m b/3DIMC/IMCComputationOnMask.m index b2757ab..ef6eddb 100644 --- a/3DIMC/IMCComputationOnMask.m +++ b/3DIMC/IMCComputationOnMask.m @@ -786,6 +786,51 @@ -(float *)createImageForMaskWithCellData:(float *)data maskOption:(MaskOption)op return img; } +-(UInt8 *)createImageForCategoricalMaskWithCellDataIndex:(NSUInteger)index maskType:(MaskType)maskType{ + if(!self.mask.mask) + return NULL; + + int * copy = copyMask(self.mask.mask, (int)self.mask.imageStack.width, (int)self.mask.imageStack.height); + UInt8 * img = calloc(self.mask.imageStack.numberOfPixels, sizeof(UInt8)); + + if(self.computedData == NULL) + return NULL; + float * data = self.computedData[index]; + + NSInteger pix = self.mask.imageStack.numberOfPixels; + + for (NSInteger i = 0; i < pix; i++) { + if(copy[i] == 0)continue; + NSInteger index = abs(copy[i]) - 1; + + + if(maskType == MASK_ALL_CELL){ + img[i] = data[index]; + copy[i] = abs(copy[i]); + } + + else if(maskType == MASK_CYT){ + img[i] = MAX((copy[i] > 0) * data[index], 0); + copy[i] = copy[i] > 0?copy[i]:0; + } + + else if(maskType == MASK_NUC){ + img[i] = MAX((copy[i] < 0) * data[index], 0); + copy[i] = copy[i] < 0?-copy[i]:0; + } + else if(maskType == MASK_NUC_PLUS_CYT){ + img[i] = (copy[i]/abs(copy[i])) * data[index]; + } + } + + for (NSInteger i = 0; i < pix; i++) + if(copy[i] == 0) + img[i] = 0; + + free(copy); + return img; +} + -(CGImageRef)coloredMaskForChannel:(NSInteger)channel color:(NSColor *)color maskOption:(MaskOption)option maskType:(MaskType)maskType maskSingleColor:(NSColor *)maskSingleColor brightField:(BOOL)brightField{ if(channel == NSNotFound) return NULL; diff --git a/3DIMC/IMCFileExporter.h b/3DIMC/IMCFileExporter.h index ad5d78c..b4c172b 100644 --- a/3DIMC/IMCFileExporter.h +++ b/3DIMC/IMCFileExporter.h @@ -12,6 +12,7 @@ @class IMCScrollView; @class IMCComputationOnMask; @class IMCLoader; +@class IMCPixelClassification; @interface IMCFileExporter : NSObject @@ -26,6 +27,9 @@ //this is a subroutine, but useful to expose +(void)writeArrayOfRefImages:(NSArray *)images withTitles:(NSArray *)titles atPath:(NSString *)path in16bits:(BOOL)sixteenBits;//Array of ImageRefs multipage +//Mask saving ++(void)saveMask:(IMCComputationOnMask *)mask channel:(NSInteger)channel path:(NSString *)path; + //JPEG quick saving +(void)saveJPEGFromScroll:(IMCScrollView *)scroll withPath:(NSString *)fullPath allOrZoomed:(BOOL)zoomed; +(void)copyToClipBoardFromScroll:(IMCScrollView *)scroll allOrZoomed:(BOOL)zoomed; diff --git a/3DIMC/IMCFileExporter.m b/3DIMC/IMCFileExporter.m index 8e5cf74..3de8e61 100644 --- a/3DIMC/IMCFileExporter.m +++ b/3DIMC/IMCFileExporter.m @@ -86,10 +86,8 @@ +(void)writer:(TIFF *)tiff writeBuffer:(unsigned char *)buffer width:(int)width TIFFSetField(tiff, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE); } - int bytesPix = 32/bitsPixel;//Improve RCF - + int bytesPix = 8/bitsPixel;//Improve RCF for (int i = 0; i < height; i++) { - TIFFWriteScanline(tiff, &buffer[i * width * bytesPix], i, 0);//1 because there is always a leading 0 } @@ -174,6 +172,21 @@ +(void)saveMultipageTiffAllChannels:(IMCImageStack *)stack path:(NSString *)path [IMCFileExporter writeArrayOfRefImages:arr withTitles:stack.channels atPath:path in16bits:YES]; } ++(void)saveMask:(IMCComputationOnMask *)mask channel:(NSInteger)channel path:(NSString *)path{ + + UInt8 * bytes = [mask createImageForCategoricalMaskWithCellDataIndex:channel maskType:MASK_ALL_CELL]; + + TIFF *writer = TIFFOpen(path.UTF8String, "w"); + + NSInteger bitsPerPixel = 8; + int samples = 1; + + [self writer:writer writeBuffer:bytes width:(int)mask.mask.imageStack.width height:(int)mask.mask.imageStack.height page:0 bpPixel:(int)bitsPerPixel samplesPerPixel:samples totalPages:1 imageName:[mask.channels objectAtIndex:channel]]; + + TIFFClose(writer); + free(bytes); +} + +(NSImage *)getNSImageForIMCScrollView:(IMCScrollView *)scroll zoomed:(BOOL)zoomed{ NSImage *someImage; if(zoomed) diff --git a/3DIMC/IMCFileWrapper.m b/3DIMC/IMCFileWrapper.m index 85eea37..dd8ea44 100644 --- a/3DIMC/IMCFileWrapper.m +++ b/3DIMC/IMCFileWrapper.m @@ -132,12 +132,17 @@ -(void)populateJsonDictForSingleImageFile:(NSString *)path success:(BOOL *)succe } else imageStack = (IMCImageStack *)self.children.firstObject.children.firstObject; - NSLog(@"%@", path); + if([imageStack hasTIFFBackstore]){ path = [imageStack backStoreTIFFPath]; - NSLog(@"%@", path); + } NSData *data = [NSData dataWithContentsOfFile:path]; + NSError * error = nil; + data = [NSData dataWithContentsOfFile:path options:0 error:&error]; + if(error) + NSLog(@"Error ---- > %@ %@", error, error.userInfo); + NSLog(@"%@",data); [self loadSingleFiler:imageStack data:data path:path success:success]; } diff --git a/3DIMC/IMCImageGenerator.m b/3DIMC/IMCImageGenerator.m index 3ca701c..512a766 100644 --- a/3DIMC/IMCImageGenerator.m +++ b/3DIMC/IMCImageGenerator.m @@ -700,7 +700,7 @@ +(CGImageRef)imageRefWithArrayOfCGImages:(NSMutableArray *)array width:(NSIntege CGImageRef refi = (__bridge CGImageRef)last; CGRect framePaint = CGRectMake(0, 0, width, height); CGContextDrawImage(canvas, framePaint, refi); - CGImageRelease(refi); +// CGImageRelease(refi); } //ref = CGBitmapContextCreateImage (canvas); //This leaked a lot changed to... diff --git a/3DIMC/IMCPixelClassification.m b/3DIMC/IMCPixelClassification.m index b90675b..f024a19 100644 --- a/3DIMC/IMCPixelClassification.m +++ b/3DIMC/IMCPixelClassification.m @@ -127,6 +127,10 @@ -(BOOL)loadMask{ return NO; } +-(BOOL)saveMaskForExternalRegistration{ + return YES; +} + -(BOOL)loadNuclearMask{ if(!self.secondRelativePath) self.secondRelativePath = diff --git a/3DIMC/IMCWorkSpace.mm b/3DIMC/IMCWorkSpace.mm index e667805..98b3727 100644 --- a/3DIMC/IMCWorkSpace.mm +++ b/3DIMC/IMCWorkSpace.mm @@ -554,6 +554,40 @@ -(IBAction)saveMultiPageTIFFs:(NSButton *)sender{ } }]; } + +-(IBAction)savePixelClassificationsAsTIFFs:(NSButton *)sender{ + + + NSOpenPanel *panel = [NSOpenPanel openPanel]; + panel.canChooseFiles = NO; + panel.canChooseDirectories = YES; + panel.canCreateDirectories = YES; + [panel beginSheetModalForWindow:self.windowForSheet completionHandler:^(NSInteger result){ + if (result == NSModalResponseOK) + for (IMCComputationOnMask *comp in self.inScopeComputations.copy) { + BOOL wasLoaded = comp.isLoaded; + if(!wasLoaded) + [comp loadLayerDataWithBlock:nil]; + while (!comp.isLoaded); + if([panel.URL.path isEqualToString:self.dataCoordinator.filePath]) + { + [General runAlertModalWithMessage:@"Choose a directory different to the main project directory"]; + }else if(self.channels.selectedRow == NSNotFound){ + [General runAlertModalWithMessage:@"One channel must be selected"]; + } + else + { + NSLog(@"____"); + [IMCFileExporter saveMask:comp channel:self.channels.selectedRow path:[panel.URL.path + stringByAppendingPathComponent:[comp.mask.imageStack backStoreTIFFPath].lastPathComponent]]; + + if(!wasLoaded) + [comp unLoadLayerDataWithBlock:nil]; + } + } + }]; +} + -(IBAction)saveMultiPageTIFFsWithSelected:(NSButton *)sender{ if(![self checkThereIsImageInScopeAndChannelsSelected:YES]) diff --git a/3DIMC/IMCWorkspaceSelector.m b/3DIMC/IMCWorkspaceSelector.m index 9e4c6b8..29ac205 100644 --- a/3DIMC/IMCWorkspaceSelector.m +++ b/3DIMC/IMCWorkspaceSelector.m @@ -408,10 +408,12 @@ -(NSMenu *)tableView:(NSTableView *)aTableView menuForRows:(NSIndexSet *)rows{ NSArray *titles = @[@"Export FCS file...", @"Add features from Cell Profiler file...", + @"Save mask..." //@"Convert to mask" ]; NSArray *selectors = @[@"exportFCSFile...", @"addFeaturesFromCP:", + @"savePixelClassificationsAsTIFFs:" // @"convertToMask:" ]; [addingTitles addObjectsFromArray:titles]; diff --git a/histoCAT++3D.entitlements b/histoCAT++3D.entitlements new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/histoCAT++3D.entitlements @@ -0,0 +1,5 @@ + + + + +