Search code examples
imagematlabimage-processinganglequantitative

Convert angle quantitative data into qualitative images


I am a crystallographer trying to analyse crystals orientations from up to 5000 files. Can Matlab convert angle values in a table that look like this:

enter image description here

Into a table that looks like this?:

enter image description here


Solution

  • Here's a more concrete example based on Lakesh's idea. However, this will handle any amount of rotation. First start off with a base circular image with a strip in the middle. Once you do this, simply run a for loop that stacks all of these rotated images in a grid that resembles the angles seen in your rotation values matrix for every rotation angle that we see in this matrix.

    The trick is to figure out how to define the base orientation image. As such, let's define a white square, with a black circle in the middle. We will also define a red strip in the middle. For now, let's assume that the base orientation image is 51 x 51. Therefore, we can do this:

    %// Define a grid of points between -25 to 25 for both X and Y
    [X,Y] = meshgrid(-25:25,-25:25);
    
    %// Define radius
    radius = 22;
    
    %// Generate a black circle that has the above radius
    base_image = (X.^2 + Y.^2) <= radius^2;
    
    %// Make into a 3 channel colour image
    base_image = ~base_image;
    base_image = 255*cast(repmat(base_image, [1 1 3]), 'uint8');
    
    %// Place a strip in the middle of the circle that's red
    width_strip = 44;
    height_strip = 10;
    strip_locs = (X >= -width_strip/2 & X <= width_strip/2 & Y >= -height_strip/2 & Y <= height_strip/2);
    base_image(strip_locs) = 255;
    

    With the above, this is what I get:

    enter image description here

    Now, all you need to do is create a final output image which has as many images as we have rows and columns in your matrix. Given that your rotation matrix values are stored in M, we can use imrotate from the image processing toolbox and specify the 'crop' flag to ensure that the output image is the same size as the original. However, with imrotate, whatever values don't appear in the image after you rotate it, it defaults to 0. You want this to appear white in your example, so we're going to have to do a bit of work. What you'll need to do is create a logical matrix that is the same size as the input image, then rotate it in the same way like you did with the base image. Whatever pixels appear black (which are also false) in this rotated white image, these are the values we need to set to white. As such:

    %// Get size of rotation value matrix
    [rows,cols] = size(M);
    
    %// For storing the output image
    output_image = zeros(rows*51, cols*51, 3);
    
    %// For each value in our rotation value matrix...
    for row = 1 : rows
        for col = 1 : cols
            %// Rotate the image
            rotated_image = imrotate(base_image, M(row,col), 'crop');
    
            %// Take a completely white image and rotate this as well.
            %// Invert so we know which values were outside of the image
            Mrot = ~imrotate(true(size(base_image)), M(row,col), 'crop');
    
            %// Set these values outside of each rotated image to white
            rotated_image(Mrot) = 255;
    
            %// Store in the right slot.
            output_image((row-1)*51 + 1 : row*51, (col-1)*51 + 1 : col*51, :) = rotated_image;
        end
    end
    

    Let's try a few angles to be sure this is right:

    M = [0 90 180; 35 45 60; 190 270 55];
    

    With the above matrix, this is what I get for my image. This is stored in output_image:

    enter image description here


    If you want to save this image to file, simply do imwrite(output_image, 'output.png');, where output.png is the name of the file you want to save to your disk. I chose PNG because it's lossless and has a relatively low file size compared to other lossless standards (save JPEG 2000).

    Edit to show no line when the angle is 0

    If you wish to use the above code where you want to only display a black circle if the angle is around 0, it's just a matter of inserting an if statement inside the for loop as well creating another image that contains a black circle with no strip through it. When the if condition is satisfied, you'd place this new image in the right grid location instead of the black circle with the red strip.

    Therefore, using the above code as a baseline do something like this:

    %// Define matrix of sample angles
    M = [0 90 180; 35 45 60; 190 270 55];
    
    %// Define a grid of points between -25 to 25 for both X and Y
    [X,Y] = meshgrid(-25:25,-25:25);
    
    %// Define radius
    radius = 22;
    
    %// Generate a black circle that has the above radius
    base_image = (X.^2 + Y.^2) <= radius^2;
    
    %// Make into a 3 channel colour image
    base_image = ~base_image;
    base_image = 255*cast(repmat(base_image, [1 1 3]), 'uint8');
    
    %// NEW - Create a black circle image without the red strip
    black_circle = base_image;
    
    %// Place a strip in the middle of the circle that's red
    width_strip = 44;
    height_strip = 10;
    strip_locs = (X >= -width_strip/2 & X <= width_strip/2 & Y >= -height_strip/2 & Y <= height_strip/2);
    base_image(strip_locs) = 255;
    
    %// Get size of rotation value matrix
    [rows,cols] = size(M);
    
    %// For storing the output image
    output_image = zeros(rows*51, cols*51, 3);
    
    %// NEW - define tolerance
    tol = 5;
    
    %// For each value in our rotation value matrix...
    for row = 1 : rows
        for col = 1 : cols
    
            %// NEW - If the angle is around 0, then draw a black circle only
            if M(row,col) >= -tol && M(row,col) <= tol
                rotated_image = black_circle;
            else %// This is the logic if the angle is not around 0
                 %// Rotate the image
                rotated_image = imrotate(base_image, M(row,col), 'crop');
    
                %// Take a completely white image and rotate this as well.
                %// Invert so we know which values were outside of the image
                Mrot = ~imrotate(true(size(base_image)), M(row,col), 'crop');
    
                %// Set these values outside of each rotated image to white
                rotated_image(Mrot) = 255;                
            end
    
             %// Store in the right slot.
            output_image((row-1)*51 + 1 : row*51, (col-1)*51 + 1 : col*51, :) = rotated_image;
        end
    end
    

    The variable tol in the above code defines a tolerance where anything within -tol <= angle <= tol has the black circle drawn. This is to allow for floating point tolerances when comparing because it's never a good idea to perform equality operations with floating point values directly. Usually it is accepted practice to compare within some tolerance of where you would like to test for equality.

    Using the above modified code with the matrix of angles M as seen in the previous example, I get this image now:

    enter image description here

    Notice that the top left entry of the matrix has an angle of 0, which is thus visualized as a black circle with no strip through it as we expect.