Dynamical correlation between two graphics possible ?

Hello everybody :)

I'm an Igor newbie and I want to duplicate a function of another program (developed in C) in my data analyzing in Igor.

Initialy I have a 256x256 .tiff image.

With simples equations I extract in Igor from this image 2 maps :
1 map of Itensity and
1 map of Brightness.
Each of them are 256x256 waves (see attached images) and are displayed in the main window.

I vectorized the maps information (instead of 2D[256][256] waves I create 1D[65536] waves)
I plot the Brightness vs Intensity for the vectorized maps and I displayed it in the same window.



I want to know if there is a possibility to obtain dynamicaly a visual correlation between a region of the "VerSus graph" and the corresponding pixels in the images of the maps.


The square representation is the ideal display I would want to get but it's not restrictive.



As I don't really know how to ask to Igor to re-lauch a "function" (/proc/macro) each time the mouse is acting in the graphic, the only thing I managed to program is a non-dynamical defininition of a region (with 4 setvariable) and with a "button" to lauch a function.
The function just checks the value of the brightness and of the intensity and creates a mask to change the pixels in red.

It's very time consuming.

Does anyone know if it's possible to ask to Igor to maintain the execution of the program in a "waiting" state in order to lauch a loop when interacting with a graphic ?


Thank you very much

The attached files are :

01 - The Intensity map
02 - The Brightness map
03 - The Brightness versus Intensity graphic extract from the 2 vectorised maps
04 - The display of the window with the 3 elements.
05 - Example number one of the correleation between a defined square of pixels of the Versus Graph and the corresponding pixels in the 2 maps
06 - Same example but with the square moved to the right.
07 - Igor project with the differents waves.
01 - Intensity map.jpg (47.93 KB) 02 - Brightness map.jpg (68.51 KB) 03 - Versus Graphic.jpg (16.99 KB) 04 - Raw Display.jpg (123.78 KB) 05 - Display with the square.jpg (120.87 KB) 06 - Display with the square moved.jpg (120.98 KB) 07 - Simplified experiment.pxp (1.05 MB)
I have code that pretty much does what you want but it is integrated in a larger package and I cannot promise to extract the important bits and pieces in a short amount of time. And it's different in the way that I work with maps containing integers, which has consequences for my approach. That is:

- Redimension both maps from 2D into 1D
- Create a bivariate Histogram (see e.g. http://www.igorexchange.com/node/4651). Here my approach is that the bin size for the histogram is always 1 count (of intensity)
- Plot histogram as NewImage
- Then I use a region of interest (ROI) tool (under image analysis) to select an area on the histogram (see picture), which means creating a mask with 1 for "within the ROI" and 0 for outside
- After multiplication of the mask with the histogram I am left with pairs, say X and Y, of intensities of two elements
- Now the task is to find the pixels in the initial 2D images where X AND Y counts occur. This is simplified by using a Cantor pairing function to generate one unique integer out of two: http://en.wikipedia.org/wiki/Pairing_function
- now the XY pairs from the histogram and the two 2D maps are "cantored"
- then I need to find the correct Cantor values. For this I got input here: http://www.igorexchange.com/node/5447 , although my final version differs from what I wrote back there

All this is implemented in a graphical user interface (see image). The visualisation of the pixels is executed when clicking the "Get Pixels" button. Initially I wanted this automatically to run once the polygon from the ROI action closes, but I didn't find a way to do that.

Hope this gives you an idea but maybe there are better ways.
IntensityMap.png (81.2 KB)
Hello,

Thank you for your answer.

If I understand well, you are suggesting to transform my "versus graphic" into an image in order to "Cantor pairing" the pixels.

My little problem with this option is that the rest of my program is interacting with the data inside the "versus graphic" and I want to keep it dynamically updated when changing parameters (Threshold of intensity and brightness for example).

* The correlation between the pixels of the "maps images" and the pixels of my "versus graphic" is obvious in my case :
each pixel of the map has a [i][j] coordinate (256x256).
In the "Versus graphic" made from the vectorized maps each point has an index between 0 and 65536.
I can extract the coordinate in [i][j] of each point of my "versus graphic" doing this : [i] = floor (index/256) and [j] = mod(index, 256)


* If I manage to draw a rectangle in this graphic, when selecting several point with this rectangle, I will obtain their coordinate in the maps directly with their index.

But I don't know how to draw a rectangle in my "versus graphic" (and I don't know if it's possible) without changing it into image (to keep the live update I need). Moreover, I don't know how to identify the points selected in the graphic (like the masqueetomask tool which creates a mask with 0 and 1).



I'll keep going to find a solution and I'll try to adapt yours but, even if your work seems very close to mine, I'm not sure it will fit with my needs.

Thank you again :)
Djebulon wrote:

If I understand well, you are suggesting to transform my "versus graphic" into an image in order to "Cantor pairing" the pixels.
My little problem with this option is that the rest of my program is interacting with the data inside the "versus graphic" and I want to keep it dynamically updated when changing parameters (Threshold of intensity and brightness for example).



Looking closer at your data it is probably not possible to easily adapt my approach to your data. The pairing works for integers, which in my case typically are between 0 and a few hundred (max) for both input images. The main advantage is to speed up the look-up process to find the desired pixel coordinates. The above approach is virtually instant for images up to 512*384 pixels (larger not tested). For me it is important to have the density plots because I'm looking for distinct populations with high pixel abundances of count pairs.


Djebulon wrote:

But I don't know how to draw a rectangle in my "versus graphic" (and I don't know if it's possible) without changing it into image (to keep the live update I need). Moreover, I don't know how to identify the points selected in the graphic (like the masqueetomask tool which creates a mask with 0 and 1).


This you can do using FindPointsInPoly and GraphWaveDraw, which can be used to achieve pretty much the same as the ROI mask does on the 2D histogram.


Thank you very much for these 2 functions.

I will try to see if I can adapt them to my problem but they seem promissing.
Well, here is the piece of code that finds the pixels. This assumes that the histogram and the ROI mask already exist. Maybe you can modify it to fit your needs.

function/WAVE  PixFromHistogram(xw, yw, M_Hist, M_ROIMask)  
    wave xw, yw, M_Hist, M_ROIMask

    variable rows = DimSize(xw, 0)
    variable cols = DimSize(xw, 1) 
   
    // Set pixels outside ROI to zero
    MatrixOP/FREE/O M_PixHistROI = M_Hist * M_ROIMask
   
    // convert ROI and histogram information into into 1D waves, keep index information on intensity   
        variable rowsHist = DimSize(M_PixHistROI , 0)
    variable colsHist = DimSize(M_PixHistROI , 1)
    Make/FREE/O/N=(rowsHist * colsHist) nPix, countsX, countsY
    Multithread nPix = M_PixHistROI[p]
    Multithread countsY= floor(p / rowsHist )
    Multithread countsX = mod(p, rowsHist) 
    sort nPix, nPix, countsX, CountsY
   
    // condese waves to rows that contain pixels
    Multithread nPix = nPix == 0 ? NaN : nPix
    variable MinCount = WaveMin(nPix)
    Findlevel/P/Q nPix, MinCount
    DeletePoints 0, (V_LevelX), nPix, countsX, countsY  
   
    variable nXYpairs = numPnts(nPix)
   
    // make waves to keep results vom Cantor function
    Make/FREE/O/I/U/N=(nXYPairs) ROISolutions
    Make/FREE/O/I/U/N=(rows * cols)  MapSolutions
   
    // make unique integers using cantor pairing function
    MultiThread ROISolutions = Cantor(countsX[p], countsY[p])
    MultiThread MapSolutions = Cantor(xw[p], yw[p])
   
    // Make result wave unsigned 8 bit to make it compatible with ImageMorphology
    Make/O/B/U/N=(rows * cols) M_PixFromDensityPlot = 0  

    // make a point order wave and sort cantor integers
    Make/FREE/O/N=(numPnts(MapSolutions)) PntOrder = p
    Sort MapSolutions MapSolutions, PntOrder
    Sort ROISolutions ROISolutions


    // clip everything outside the WaveMin(ROIsolutions) to WaveMax(ROIsolutions) range in the map wave
    FindLevel/Q/P MapSolutions,  WaveMin(ROISolutions)
    DeletePoints 0, V_LevelX, MapSolutions, PntOrder   
    FindLevel/Q/P MapSolutions, WaveMax(ROISolutions)+1
    DeletePoints V_LevelX, inf, MapSolutions, PntOrder

    variable np = numpnts(ROISolutions)
    variable length = numpnts(MapSolutions)
    Make/FREE/O/N=(length) tmp=NaN
   

    //Find values from ROI in 1D Cantor wave MapSolutions
    // BinarySearchInterp returns the point number if value is present in a wave or a fractional, interpolated pointnumber if value is not present
    // use Mod(value, 1) to determine whether return value has a remainer when devided by 1  
    Multithread tmp = mod(binarySearchInterp(ROISolutions, MapSolutions[p]), 1) == 0 ? 1 : NaN
   
    // eliminate points in PntOrder which are NaN
    Multithread PntOrder *= tmp
    WaveTransform zapNaNs PntOrder

    // Fill output wave in 1D with 1 for pixels in ROI
    variable i
    for (i=0; i<numPnts(PntOrder); i+= 1)      
        M_PixFromDensityPlot[PntOrder[i]] = 1
    endfor
   
    // make 2D image from 1D wave  
    Redimension /N=(rows,cols) M_PixFromDensityPlot
       
    Return M_PixFromDensityPlot
end

ThreadSafe function Cantor(aa, bb)
    variable aa, bb
   
    variable sumTerm  = aa + bb
    return  1/2 * (sumTerm) * (sumTerm + 1) + bb
end


The attached experiment shows an example. Execute

PixFromHistogram(Al, Si, M_Hist, M_ROIMask)

to get a mask of the pixels of interest.

You can play with different ROI's on the histogram using "Analysis -> Packages -> Image Processing" then "Image -> ROI". Make sure you uncheck "Zero ROI pixels" in the panel.
ShowPixels.pxp (203.14 KB)
Thank you very much for you code !!!

Okay it seems that I have a lot of work to adapt it to my issue :S

But with  FindPointsInPoly and GraphWaveDraw and with your code, I will find a way to do it.


I'll let you know when it will be done ...


Thank you again !
Djebulon wrote:
...But I don't know how to draw a rectangle in my "versus graphic" (and I don't know if it's possible) without changing it into image (to keep the live update I need). Moreover, I don't know how to identify the points selected in the graphic (like the masqueetomask tool which creates a mask with 0 and 1).


Have you looked into using the Marquee for this?
Yes and what I wanted to do initially was to draw a rectangle and to be able to drag it with the mouse. (it's possible with the marquee but the rectangle hasn't any interaction with the data stored in waves)
With the marquee option, you can have a rectangle but you just identify the coordinates once. (right clicking and doing marquee2mask)

When it's done, I didn't find a way to shift the rectangle and to act dynamically on the image displaying the identified pixels (when dragging the rectangle)

Djebulon wrote:
I didn't find a way to shift the rectangle and to act dynamically on the image displaying the identified pixels (when dragging the rectangle)


I just tried this on my code:

Function HistogramHook(s)
    STRUCT WMWinHookStruct &s
   
    switch(s.eventCode)
       
        case 8:                
           
        print "modified"
                // run code to save ROI using ImageGenerateROIMask and to extract pixels
       
        break
                       
    endswitch
   
    return 0
End


This works kind of dynamically after dragging the ROI and release of the mouse button, not while the ROI is moving. But there are side effects because this action may be triggered by other events than moving the ROI.


Thank you for all your answer !

I managed to get almost what I wanted using a little trick and only buttons.
I don't drag the rectangle but I shift it.

Thank you Chrlie for these 2 functions :  FindPointsInPoly and  GraphWaveDraw

-------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Allows the drawing of a polygone inside the Versus Graphic
Function SquareDRAW(Ctrlname) : ButtonControl
    String Ctrlname

    Variable k
    String/G VSGraph
    Variable/G BooleanDraw
    Wave xWavePoly, yWavePoly, W_inPoly, PixelsToEnlight
   
    k=1
    if (BooleanDraw==1)             // If the SquareDraw button has already been activated
        DoAlert/T="Warning" 1, "The Polygone has already be drawn ! \r\n Do you want to erase it and draw another one ?"
        if (V_flag==2)
            k=0
        endif
    endif
   
    Switch (k)
        case 1:                     // Draw a new polygone
           
            W_inPoly = 0
            PixelsToEnlight = 0
   
            SetDrawLayer/W=Prog_Window#BvsI/K UserBack
            SetDrawLayer/W=Prog_Window#BvsI UserBack
            GraphWaveDraw/W=Prog_Window#BvsI/O yWavePoly, xWavePoly
           
            BooleanDraw = 1
           
            break
       
        case 0:                     // Don't draw a new polygone
           
            break
    Endswitch

End

-------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Identifies the pixels inside polygone of the Versus Graphic 
Function SquareDONE(Ctrlname) : ButtonControl
    String Ctrlname

    Variable i, k
    Variable/G Rows, Columns, BooleanDraw
    Wave xWavePoly, yWavePoly, VInt, VBright, W_inPoly, PixelsToEnlight
   
    k=1
    if (BooleanDraw==0)             // If the SquareDraw button has NOT already been activated
        DoAlert/T="Warning" 0, "The Polygone has to be drawn first !"
        k=0
    endif
   
    Switch (k)
        case 1:                     // Indentifies the pixels inside the drawn polygone
           
            FindPointsInPoly VInt, VBright, xWavePoly, yWavePoly
            GraphNormal/W=Prog_Window#BvsI
   
            For (i=0;(i<(Rows*Columns));i+=1)
       
                if (W_inPoly[i]==1)
                    PixelsToEnlight[floor(i/columns)][mod(i, columns)] = 65536
                endif
            Endfor
            BooleanDraw = 0
           
            break
       
        case 0:                     // No polygone drawn => no identification
           
            break
    Endswitch
   
End

-------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Displays the selected pixels inside the Intensity map
Function CorrelationON(Ctrlname) : ButtonControl
    String Ctrlname

    Variable k
    Variable/G BooleanCorrel, BooleanDraw
    Wave Intensity, PixelsToEnlight
   
    k=1
    if (BooleanDraw==1)
        SquareDONE(Ctrlname)
    endif
   
    if (BooleanCorrel==1)               // If the CorrelON button has already been activated
        DoAlert/T="Warning" 0, "Selected pixels have already been displayed !"
        k=0
    endif
   
    Switch (k)
        case 1:                     // Creates the matrix of pixels to display and superimposes it on the map
           
            ModifyImage/W=Prog_Window#Map1 Intensity ctab= {0.001,*,Grays,1};DelayUpdate
            ModifyImage/W=Prog_Window#Map1 Intensity minRGB=(52224,52224,52224),maxRGB=0
            AppendImage/W=Prog_Window#Map1 PixelsToEnlight; DelayUpdate
            ModifyImage/W=Prog_Window#Map1 PixelsToEnlight ctab= {0.01,*,CyanMagenta,0};DelayUpdate
            ModifyImage/W=Prog_Window#Map1 PixelsToEnlight minRGB=NaN,maxRGB=0
           
            BooleanCorrel = 1
           
            break
       
        case 0:                     // Matrix already displayed
           
            break
    Endswitch
   
End

-------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Restore the intensity map.
Function CorrelationOFF(Ctrlname) : ButtonControl
    String Ctrlname

    Variable k
    Variable/G BooleanDraw, BooleanCorrel
    Wave Intensity, PixelsToEnlight
   
    k=1
    if (BooleanDraw==1)
        SquareDONE(Ctrlname)
    endif
   
    if (BooleanCorrel==0)               // If the CorrelOFF button has already been activated
        DoAlert/T="Warning" 0, "Intensity map is already in normal mode"
        k=0
    endif
   
    Switch (k)
        case 1:                     // Removes the selected pixels from the intensity map
           
            RemoveImage/W=Prog_Window#Map1 PixelsToEnlight
            ModifyImage/W=Prog_Window#Map1 Intensity  ctab= {*,*,SpectrumBlack,0};DelayUpdate
            ModifyImage/W=Prog_Window#Map1 Intensity minRGB=0,maxRGB=0
   
            BooleanCorrel = 0
           
            break
       
        case 0:                     // Selected pixels already removed
           
            break
    Endswitch
   
End
-------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------









-------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Function Left(Ctrlname) : ButtonControl
    String Ctrlname

    Variable i, k
    Variable/G RectRadioVal
    Variable/G BooleanDraw, BooleanCorrel, Xincrement, Rows, Columns
    Wave xWavePoly, yWavePoly, W_inPoly, PixelsToEnlight, VInt, VBright
   
    k=1
    if (BooleanDraw==1)
        DoAlert/T="Warning" 0, "Pixels inside the Polygone have to be identified first with the SquarDONE button"
        k=0
    endif
    if (BooleanCorrel==0)
        DoAlert/T="Warning" 0, "The correlation between Versus graphic and map should be enable first"
        k=0
    endif
   
    Switch (k)
        case 1:                     // translate the polygone to the left
   
            If (RectRadioVal ==1)                               // Rectangle shift selected
           
                For (i=0;(i<dimsize(xWavePoly, 0));i+=1)
                    xWavePoly[i] = xWavePoly[i] - Xincrement
                Endfor
            Endif
           
            If (RectRadioVal==2)                                // Rectangle size increasement selected
                xWavePoly[0] = xWavePoly[0]-Xincrement
                xWavePoly[3] = xWavePoly[3]-Xincrement
                xWavePoly[4] = xWavePoly[4]-Xincrement
            Endif
               
            RemoveImage/W=Prog_Window#Map1 PixelsToEnlight
            PixelsToEnlight = 0
            W_inPoly = 0
            FindPointsInPoly VInt, VBright, xWavePoly, yWavePoly
           
            For (i=0;(i<(Rows*Columns));i+=1)
                if (W_inPoly[i]==1)
                    PixelsToEnlight[floor(i/columns)][mod(i, columns)] = 65536
                endif
            Endfor
       
            AppendImage/W=Prog_Window#Map1 PixelsToEnlight; DelayUpdate
            ModifyImage/W=Prog_Window#Map1 PixelsToEnlight ctab= {0.01,*,CyanMagenta,0};DelayUpdate
            ModifyImage/W=Prog_Window#Map1 PixelsToEnlight minRGB=NaN,maxRGB=0
       
            break
   
        case 0:                     // No translation due to inactivation of Square or Correlation
       
            break
    Endswitch
   
       
End
-------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------


And four buttons and associated functions are created : Left, Right, Up and Down with 2 setvariable : x-increment and y-increment.

If you think you will be intersted in this non-complete method, I can simplify my procedure file and post a saved experiment here with an exemple.