#include #include #include #include #include #include #include #define TAG 50839 #define OFFSET 16 #define ONE_ROW 1 #define bool_t uint8_t #define FALSE 0 #define TRUE 1 //----------------------------------------------- // Difference in Time // Compute the difference between timespec structs double diff_timespec(struct timespec *time1, struct timespec *time0) { return (time1->tv_sec - time0->tv_sec) + (time1->tv_nsec - time0->tv_nsec) / 1000000000.0; } //----------------------------------------------- // Color Equal to Background // Background: zeros in RGB, alpha can be whatever bool_t color_zero(uint8_t* color1, size_t channels) { for (size_t idx = 0; idx < channels; idx++) { if (idx == 3) { break; } if (color1[idx] != 0) { return FALSE; } } return TRUE; } //----------------------------------------------- // Color Equality // Determine if two colors are identical bool_t color_equal(uint8_t* color1, uint8_t* color2, size_t channels) { for (size_t idx = 0; idx < channels; idx++) { if (color1[idx] != color2[idx]) { return FALSE; } } return TRUE; } //----------------------------------------------- // Print out the `channels` length color vector void print_color(uint8_t* color, size_t channels) { for (size_t index = 0; index < channels; index++) { printf("%hhu ", color[index]); } printf("\n"); } //----------------------------------------------- // Flood // Floods a mask from a given set of image to determine the contiguous regions // 1. Check that the (x,y) is within the picture // 2. Check if the (x,y) coordinate in the mask is unused // 3. Check if the (x,y) coordinate in the image is non-background // 4. Check if the (x,y) coordinate in the image is the same color as the fill color // 5. If all hold, set the label for the pixel, and check each neighbor // Otherwise, stop flooding bool_t flood(uint8_t* image, uint16_t* mask, size_t width, size_t height, size_t channels, size_t x, size_t y, uint8_t* fill_color, uint16_t label) { if ((x >= width) | (y >= height)) { return FALSE; } size_t coord = x + y*width; if (mask[coord] != 0) { return FALSE; } if (color_zero(&(image[coord*channels]), channels)) { return FALSE; } if (color_equal(&(image[coord*channels]), fill_color, channels)) { mask[coord] = label; flood(image, mask, width, height, channels, x, y+1, fill_color, label); flood(image, mask, width, height, channels, x, y-1, fill_color, label); flood(image, mask, width, height, channels, x+1, y, fill_color, label); flood(image, mask, width, height, channels, x-1, y, fill_color, label); return TRUE; } return FALSE; } int main() { //----------------------------------------------- //-RAYLIB-INIT //----------------------------------------------- SetTraceLogLevel(LOG_ERROR); SetConfigFlags(FLAG_WINDOW_RESIZABLE); const char* gui_title = "Image Manip - Useful for segmentations!"; InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, gui_title); //----------------------------------------------- //----------------------------------------------- //-TIFF-IMAGE-OPEN------------------------------- //----------------------------------------------- TIFF *tif = TIFFOpen("./data/test.tif", "r"); if (!tif) { fprintf(stderr, "Failed to open TIFF file\n"); return 1; } //----------------------------------------------- //----------------------------------------------- //-TIFF-FIND-DIMENSIONS-------------------------- //----------------------------------------------- uint32_t width, height; 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; } //----------------------------------------------- printf("Total Strip Size: %llu\n", STRIP_LENGTH*STRIP_COUNT); printf("Total Image Size: %lu in %hhu channel(s)\n", width*height*channels, channels); assert(STRIP_LENGTH*STRIP_COUNT == width*height*channels); //----------------------------------------------- //-TIFF-LOAD-DATA-------------------------------- //----------------------------------------------- void* buffer = malloc(STRIP_LENGTH*sizeof(uint8_t)); uint8_t* raster_8 = calloc(width*height*channels,sizeof(uint8_t)); if (raster_8 == NULL) { fprintf(stderr, "Memory allocation error\n"); TIFFClose(tif); return 1; } 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++) { raster_8[x + y*STRIP_LENGTH] = ((uint8_t*)buffer)[x]; } } free(buffer); //----------------------------------------------- struct timespec ts_start, ts_end; timespec_get(&ts_start, TIME_UTC); //----------------------------------------------- //-FLOOD-FILL-SEGMENTATION----------------------- //-CONTIGUOUS-REGION-FINDING--------------------- //----------------------------------------------- uint16_t starting_label = 1; uint16_t *labels = NULL; labels = (uint16_t*)calloc(width*height,sizeof(uint16_t)); if (labels == NULL) { fprintf(stderr, "Memory allocation error\n"); free(raster_8); TIFFClose(tif); return 1; } // 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(raster_8, labels, width, height, channels, x, y, &(raster_8[coord*channels]), starting_label)) { starting_label += 1; } } } // (When treating this as RGBA, last bits should ensure opaque) // This assumes 4096 (2^12) > labels for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { labels[x + y*width] |= 0xFF000000; } } //----------------------------------------------- timespec_get(&ts_end, TIME_UTC); printf("Time difference: %.3fms\n", 1000*diff_timespec(&ts_end, &ts_start)); printf("N_labels: %u\n", starting_label-1); uint32_t *raster = NULL; //----------------------------------------------- //-READ-TIFF-IMAGE------------------------------- //----------------------------------------------- /* raster = (uint32_t*)_TIFFmalloc(width*height*sizeof(uint32_t)); if (raster == NULL) { fprintf(stderr, "Memory allocation error\n"); free(labels); free(raster_8); TIFFClose(tif); return 1; } if (!TIFFReadRGBAImage(tif, width, height, raster, 0)) { fprintf(stderr, "Failed to read TIFF image\n"); free(labels); free(raster_8); _TIFFfree(raster); TIFFClose(tif); return 1; } */ //----------------------------------------------- //----------------------------------------------- //-RAYLIB-IMAGE-STRUCTURING---------------------- //----------------------------------------------- Image RaylibImage; RaylibImage.width = width; RaylibImage.height = height; RaylibImage.mipmaps = 1; // Use Loaded Image RaylibImage.data = raster; RaylibImage.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; // Use Strip Image Data RaylibImage.data = raster_8; if (channels == 1) { RaylibImage.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; } else if (channels == 3) { RaylibImage.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; } else if (channels == 4) { RaylibImage.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; } else { assert(0); } // Use Contiguous Labels RaylibImage.data = labels; RaylibImage.format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; //----------------------------------------------- // Image to a Texture Texture2D RaylibTexture = LoadTextureFromImage(RaylibImage); // Scale the image to the viewport /// Source Rectangle: Original Size Rectangle sourceRec = { 0.0f, 0.0f, (float)width, (float)height }; /// Destination Rectangle: Transformed Size Rectangle destRec = { 0.0f, 0.0f, (float)SCREEN_WIDTH, (float)(SCREEN_HEIGHT-OFFSET) }; /// Location to begin drawing Vector2 origin = { (float)0, (float)-OFFSET }; // Raylib boilerplate SetTargetFPS(60); Camera2D camera = { 0 }; camera.zoom = 1.0f; // GUI Loop while (!WindowShouldClose()) { //----------------------------------------------- //-DRAWING--------------------------------------- //----------------------------------------------- BeginDrawing(); ClearBackground(RAYWHITE); BeginMode2D(camera); EndMode2D(); DrawText("Image Manip", 0, 0, OFFSET, DARKGRAY); DrawTexturePro(RaylibTexture, sourceRec, destRec, origin, (float)0, RAYWHITE); /* uint32_t x = 0x49, y = 0x4A; uint32_t dx = 0x69 - x, dy = 0x6E - y; x = (SCREEN_WIDTH*x)/width; y = SCREEN_HEIGHT-((SCREEN_HEIGHT-OFFSET)*y)/height; dx = (SCREEN_WIDTH*dx)/width; dy = SCREEN_HEIGHT-((SCREEN_HEIGHT-OFFSET)*dy)/height; DrawRectangleGradientH(x, y, dx, dy, BLUE, PURPLE); */ EndDrawing(); //----------------------------------------------- } if (labels != NULL) { free(labels); } if (raster_8 != NULL) { free(raster_8); } if (raster != NULL) { _TIFFfree(raster); } TIFFClose(tif); CloseWindow(); return 0; }