#include #include #include #include #include #include #include #include // Convert x,y coords to linear coordinate size_t xy_to_coord(size_t x, size_t y, uint32_t width, uint32_t height) { return x + y*width; } // Determine if coordinate is on a mask boundary // Assumes mask is (WxH) bool_t is_on_mask_boundary(uint16_t* mask, uint32_t width, uint32_t height, size_t x, size_t y) { size_t starting_coord = xy_to_coord(x, y, width, height); size_t proposed_position; uint16_t current_value = mask[starting_coord]; // Left neighbor if (x != 0) { proposed_position = xy_to_coord(x-1, y, width, height); if (mask[proposed_position] != current_value) { return TRUE; } } // Right neighbor if ((x+1) != width) { proposed_position = xy_to_coord(x+1, y, width, height); if (mask[proposed_position] != current_value) { return TRUE; } } if (y != 0) { proposed_position = xy_to_coord(x, y-1, width, height); if (mask[proposed_position] != current_value) { return TRUE; } } if ((y+1) != height) { proposed_position = xy_to_coord(x, y+1, width, height); if (mask[proposed_position] != current_value) { return TRUE; } } return FALSE; } // Dilate masks by one 4-connected pixel uint16_t* dilate(uint16_t* mask, uint32_t width, uint32_t height) { uint16_t *new_mask = (uint16_t*)calloc(width*height,sizeof(uint16_t)); for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { size_t current_position = xy_to_coord(x, y, width, height); if (mask[current_position] != 0) { new_mask[current_position] = mask[current_position]; continue; } size_t proposed_position; if (x != 0) { proposed_position = xy_to_coord(x-1, y, width, height); if (mask[proposed_position] != 0) { new_mask[current_position] = mask[proposed_position]; continue; } } if ((x+1) != width) { proposed_position = xy_to_coord(x+1, y, width, height); if (mask[proposed_position] != 0) { new_mask[current_position] = mask[proposed_position]; continue; } } if (y != 0) { proposed_position = xy_to_coord(x, y-1, width, height); if (mask[proposed_position] != 0) { new_mask[current_position] = mask[proposed_position]; continue; } } if ((y+1) != height) { proposed_position = xy_to_coord(x, y+1, width, height); if (mask[proposed_position] != 0) { new_mask[current_position] = mask[proposed_position]; continue; } } } } return new_mask; } // Erode masks by one 4-connected pixel uint16_t* erode(uint16_t* mask, uint32_t width, uint32_t height) { uint16_t *new_mask = (uint16_t*)calloc(width*height,sizeof(uint16_t)); for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { size_t current_position = xy_to_coord(x, y, width, height); if (mask[current_position] != 0) { new_mask[current_position] = mask[current_position]; continue; } bool_t erode = FALSE; size_t proposed_position; if (x != 0) { proposed_position = xy_to_coord(x-1, y, width, height); if (mask[proposed_position] == 0) { new_mask[current_position] = mask[proposed_position]; continue; } } if ((x+1) != width) { proposed_position = xy_to_coord(x+1, y, width, height); if (mask[proposed_position] == 0) { new_mask[current_position] = mask[proposed_position]; continue; } } if (y != 0) { proposed_position = xy_to_coord(x, y-1, width, height); if (mask[proposed_position] == 0) { new_mask[current_position] = mask[proposed_position]; continue; } } if ((y+1) != height) { proposed_position = xy_to_coord(x, y+1, width, height); if (mask[proposed_position] == 0) { new_mask[current_position] = mask[proposed_position]; continue; } } } } return new_mask; } // Close up masks by N-pixels uint16_t* closeup(uint16_t* mask, uint32_t width, uint32_t height, size_t num_pixels) { uint16_t *new_mask = (uint16_t*)calloc(width*height,sizeof(uint16_t)); memcpy(new_mask, mask, width*height*sizeof(uint16_t)); for (size_t count = 0; count < num_pixels; count++) { uint16_t *new_labels = dilate(new_mask, width, height); free(new_mask); new_mask = new_labels; } for (size_t count = 0; count < num_pixels; count++) { uint16_t *new_labels = erode(new_mask, width, height); free(new_mask); new_mask = new_labels; } // Retain original mask at the very least for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { size_t coord = x + y*width; if (mask[coord] != 0) { if (new_mask[coord] != mask[coord]) { new_mask[coord] = mask[coord]; } } } } return new_mask; } // Combine Label Masks // For all empty spaces in the destination, put the extra label if it exists // Allocates an array if destination is unallocated uint16_t* combine_masks(uint16_t *destination, uint16_t *extra_labels, uint32_t width, uint32_t height) { if (destination == NULL) { destination = (uint16_t*)calloc(width*height, sizeof(uint16_t)); } for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { size_t coord = x + y*width; if (destination[coord] == 0) { destination[coord] = extra_labels[coord]; } } } return destination; } // Process Tif File to Labels // width, height will be overwritten with image dimensions // starting_label_p will be incremented for each label found in the image uint16_t* tif_to_labels(char* tif_file_name, uint32_t *width, uint32_t *height, uint16_t *starting_label_p) { //-TIFF-IMAGE-OPEN------------------------------- TIFF *tif = TIFFOpen(tif_file_name, "r"); if (!tif) { fprintf(stderr, "Failed to open TIFF file\n"); return NULL; } //-TIFF-FIND-DIMENSIONS-------------------------- size_t channels = 1; TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, width); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, height); tmsize_t STRIP_LENGTH = TIFFStripSize(tif); tmsize_t STRIP_COUNT = TIFFNumberOfStrips(tif); if ((*width)*(*height)*3 == STRIP_LENGTH*STRIP_COUNT) { channels = 3; } else if ((*width)*(*height)*4 == STRIP_LENGTH*STRIP_COUNT) { channels = 4; } //-TIFF-LOAD-DATA-------------------------------- void* buffer = malloc(STRIP_LENGTH*sizeof(uint8_t)); if (buffer == NULL) { fprintf(stderr, "Memory allocation error\n"); TIFFClose(tif); return NULL; } uint8_t* image_data = calloc((*width)*(*height)*channels,sizeof(uint8_t)); if (image_data == NULL) { fprintf(stderr, "Memory allocation error\n"); free(buffer); TIFFClose(tif); return NULL; } for (size_t y = 0; y < STRIP_COUNT; y++) { tmsize_t strip_size = TIFFReadRawStrip(tif, y, buffer, STRIP_LENGTH); assert(strip_size == STRIP_LENGTH); for (size_t x = 0; x < STRIP_LENGTH; x++) { image_data[x + y*STRIP_LENGTH] = ((uint8_t*)buffer)[x]; } } free(buffer); //-FLOOD-FILL-SEGMENTATION----------------------- //-CONTIGUOUS-REGION-FINDING--------------------- uint16_t *labels = NULL; labels = (uint16_t*)calloc((*width)*(*height),sizeof(uint16_t)); if (labels == NULL) { fprintf(stderr, "Memory allocation error\n"); free(image_data); TIFFClose(tif); return NULL; } // Flood fill on each pixel // Increase label for each success for (size_t y = 0; y < *height; y++) { for (size_t x = 0; x < *width; x++) { size_t coord = x + y*(*width); if(flood(image_data, labels, *width, *height, channels, x, y, &(image_data[coord*channels]), *starting_label_p)) { *starting_label_p += 1; } } } free(image_data); TIFFClose(tif); return labels; } // Convert mask to bitmap struct bitmap_t* uint16_to_bitmap(uint16_t* buffer, uint32_t width, uint32_t height) { struct pixel_t* out_buffer = (struct pixel_t*)calloc(width*height, sizeof(struct pixel_t)); if (out_buffer == NULL) { return NULL; } struct bitmap_t* bitmap = (struct bitmap_t*)malloc(sizeof(struct bitmap_t)); if (bitmap == NULL) { free(out_buffer); return NULL; } for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { size_t coord = x + y*width; uint8_t red = (buffer[coord] & 0xF00) >> 4*2; uint8_t green = (buffer[coord] & 0x0F0) >> 4*1; uint8_t blue = (buffer[coord] & 0x00F) >> 4*0; out_buffer[coord].red = red | (red << 4); out_buffer[coord].green = green | (green << 4); out_buffer[coord].blue = blue | (blue << 4); } } bitmap->image_buffer = out_buffer; bitmap->width = (size_t)width; bitmap->height = (size_t)height; return bitmap; }