Circle with cut off edges

Hello All,

I am trying to figure out the diameter of a circle, the problem is that some of the edges of the circle have been cut by a rectangle (see attached wave "mask.ibw").
I have tried to use imageanalyzeparticle, with the "/E" flag but in this case the fitted circle must have the same area as the particle itself. Here however the circle needs to have a larger area than the particle as it needs to include the area of the cut off edges.

Any help would be greatly appreciated. Thanks in advance.
Mask.ibw
Hi,

Depends on some assumptions you are willing to make. From your image, you could use Maximum X of particle - minimum X of particle as the diameter. Not a general solution, but it works on your test case. The requirement would be one particle with at least a diameter aligned to one axis in the field of view.

Andy
Hi Andy,

Wouldn't this only work if the circle was centred in the rectangle?

Cheers

It does not need to be centered, only that the diameter is captured.

Andy
I don't think Andy's solution works for your example data as the data provided does not have a full diameter in the x or y directions.

My solution is brute force and inelegant, but it appears to work. The idea is to compute the distance between all pairs of points on the boundary of the figure. If the figure includes a pair of points on a diameter, then the maximum of all the computed distances will be the diameter. If the figure doesn't include a diameter, this method will fail.

I include two variations: one where you have already used ImageAnalyzeParticles to get the pair of xy waves describing the figure boundary and a second where you begin with the binarized figure.

This method gives a diameter of 1654.21. My testing shows that a circle with the calculated diameter provides a good fit to your input mask wave.

I look forward to seeing other replies with simple and beautiful solutions.

Hope this helps.

Jeff

Method 1 (starting with boundary waves)
//Input waves should be same length and 1 Dimensional; They are an xy pair describing
//the boundary of the partial circle.
//If starting from and image, use ImageAnalyze particles to get the x and y boundary
//waves (W_BoundaryX and W_BoundaryY) for the object.
Function GetDistanceFromBoundary(wX, wY)
    Wave wX
    Wave wY
   
    Variable vNumElements
    Variable vIndex_i
    Variable vIndex_j
    Variable vIndex_d
    Variable vX
    Variable vY
    Variable vDiameter
    Variable vTest
   
    vNumElements = DimSize(wX, 0 )
    Make/FREE /N=(vNumElements^2) wDistance
    Make/FREE /N=(vNumElements^2) wNumberOfMax
   
    for( vIndex_i = 0; vIndex_i < vNumElements; vIndex_i += 1 )
        vX = wX[vIndex_i]
        vY = wY[vIndex_i]
        for( vIndex_j = 0; vIndex_j < vNumElements; vIndex_j += 1 )
            vIndex_d = vIndex_i * vNumElements + vIndex_j
            wDistance[vIndex_d] = sqrt( (wX[vIndex_j] - vX) ^2 +  (wY[vIndex_j] - vY) ^2 )
        endfor
    endfor
    vDiameter = wavemax(wDistance)
    vTest = floor(vDiameter)
    print vDiameter, vTest
    wNumberOfMax = wDistance > vTest ? 1 : 0
    print sum(wNumberOfMax)
    print "done"
End


Method 2 (starting with binarized figure)
//Input wave is 2D particle following prescription in help for ImageAnalyzeParticles:
//The source image wave must be binary, i.e., an unsigned char format where the
//particles are designated by 0 and the background by 255.
Function GetDistanceFromImage(wParticle)
    Wave wParticle
   
    imageanalyzeparticles/W stats wParticle
   
    Wave wX = W_BoundaryX
    Wave wY = W_BoundaryY
   
    Variable vNumElements
    Variable vIndex_i
    Variable vIndex_j
    Variable vIndex_d
    Variable vX
    Variable vY
    Variable vDiameter
    Variable vTest
   
    vNumElements = DimSize(wX, 0 )
    Make/FREE /N=(vNumElements^2) wDistance
    Make/FREE /N=(vNumElements^2) wNumberOfMax
   
    for( vIndex_i = 0; vIndex_i < vNumElements; vIndex_i += 1 )
        vX = wX[vIndex_i]
        vY = wY[vIndex_i]
        for( vIndex_j = 0; vIndex_j < vNumElements; vIndex_j += 1 )
            vIndex_d = vIndex_i * vNumElements + vIndex_j
            wDistance[vIndex_d] = sqrt( (wX[vIndex_j] - vX) ^2 +  (wY[vIndex_j] - vY) ^2 )
        endfor
    endfor
    vDiameter = wavemax(wDistance)
    vTest = floor(vDiameter)
    print vDiameter, vTest
    wNumberOfMax = wDistance > vTest ? 1 : 0
    print sum(wNumberOfMax)
    print "done"
End
Hi

Jeff is correct, i miss read the image a bit. An alternative solution:

1. Find the edge points

2. Discard all the ones that are on the image boundary, 1st or last row and first or last column.

3. For each row with 2 points, then calculate the average which should lie on a diameter.

4. Repeat for the columns with 2 points also lying on a diameter.

You can fit the lines and find the intersection which will be the center.

From that point you can calculate the distance to any point on edge detected to get the radius. You can do it over all the found edge points and take an average to get the benefit of statistics.

Andy