Skip to main content
Question

ZPL: Find largest / lowest 100-pixel values on NSC detector


Matteo

Hi everybody,

I would like to write a macro in which I determine the 100 largest and smallest pixel values on a detector. 
Only pixels in a certain region of interest should be considered. 

I have already successfully implemented the criterion of whether a pixel is within the ROI.

I also have a solution in mind for determining the 100 largest and smallest values, which I think should already work.

When I run the macro, the array with the values also fills up at first, but at some point it stops and no new values are added.
Do you guys have an idea why this is not working? Could there be some kind of buffer overflow or so? Or do I have a fundamental error in my macro?

I added my macro and an exemplary result below

Thanks for any help or hint :) 

 

Best,

Matteo

 

! Calculates the max and min intensity (from the 100 largest and lowest pixels) in a circle centered in the detector plane
! Calculates the uniformity = min/max from the in that circle

detector = 28
size = 51
max_radius = 25
center = 26
total = size*size
average = 100

minInt = 10000000
maxInt = -1
uniformity = 0 

idx = 1

DECLARE pixel_array, DOUBLE, 1, total
DECLARE top100, DOUBLE, 1, average
DECLARE bottom100, DOUBLE, 1, average

! NSTR 1, 0, 0, 0, 0, 1, rays, 0, 0, 0, 0

FOR x, 1, total, 1
	pixel_array(x) = NSDD(1, detector, x, 1)
	temp = pixel_array(x)
NEXT

FOR i, 1, average, 1
    top100(i) = 0
    bottom100(i) = 10000
NEXT

PRINT "----"

FOR i, 1, size, 1
	FOR j, 1, size, 1
	temp_x = i - center
	temp_y = j - center
	normalized_radius = SQRT(POWR(temp_x,2) + POWR(temp_y,2)) / max_radius

	IF (normalized_radius <= 1) 
		valueTop = pixel_array(idx)
		valueBottom = pixel_array(idx)

	    FOR y, 1, average, 1
	        IF (valueTop > top100(y)) 
	        	top100(y) = valueTop
	        	valueTop = 0
	        ENDIF

	       IF (valueBottom < bottom100(y)) 
	        	bottom100(y) = valueBottom
	        	valueBottom = 100000
	        ENDIF    
	    NEXT			
	ENDIF

    idx = idx + 1 

    NEXT	
NEXT

sum_top = 0
sum_bottom = 0

FOR y, 1, average, 1
	sum_top = sum_top + top100(y)
	sum_bottom = sum_bottom + bottom100(y)
NEXT

topMean = sum_top / average
bottomMean = sum_bottom / average

uniformityMean = bottomMean / topMean

PRINT "Mean Uniformity = ", uniformityMean

uniformity = minInt / maxInt

PRINT "-----------------"
PRINT "maxInt = ", topMean
PRINT "minInt = ", bottomMean
PRINT "Uniformity = ", uniformityMean

SETOPERAND 5, 8, topMean
SETOPERAND 7, 8, bottomMean
SETOPERAND 9, 8, uniformityMean

FOR z, 1, average, 1
	temp_1 = top100(z)
	FORMAT 4 INT
	PRINT $STR(z),
	FORMAT 6.4
	PRINT " = ", temp_1
NEXT

 

4 replies

David.Nguyen
Luminary
Forum|alt.badge.img+2

@Matteo 

 

I’m not completely sure because I don’t have a file to test, but I think the issue is with your algorithm to pick the top/bottom 100.

If we look at top 100 for example:

IF (valueTop > top100(y)) 
    top100(y) = valueTop
    valueTop = 0
ENDIF

Imagine that top100 has the following three highest pixel values (134, 121, 105, ...).

Now assume valueTop is 130.

From your code, valueTop is larger than top100(2) with value 121. Therefore, top100 becomes (134, 130, 105, ...).

But in this process, you have lost the value 121.

What should happen is that 121 and all subsequent values should be shifted so that top100 becomes (134, 130, 121, 105, ...). And the last value is dropped out of the array to preserve a size of 100.

Does it make sense? Same goes for bottom 100 (except the values should shift in the other direction).

One piece of advice, ZPL is nice to automate repetitive tasks, but when you are trying to analyze data, I’d recommend switching to the ZOSAPI, at least for the troubleshooting part were you know the algorithm might not work as intended. This will give you access to many more debugging tools.

Take care,

 

David


MichaelH
Ansys Staff
Forum|alt.badge.img+2
  • Ansys Staff
  • 387 replies
  • July 23, 2025

Hey ​@Matteo,

As David pointed out, the code for getting the Top 100 and Bottom 100 is a little faulty.  In terms of logic and readability, I would simply add a Bubble Sort algorithm to take pixel_array, sort it in ascending order, then simply taking the Nth highest and Nth lowest values of the array.  In ZPL, the Bubble Sort would look like: 

# bubble sort
FOR i = 1, total, 1
	stop = total - i
	IF stop > 0
		FOR j = 1, stop, 1
			IF pixel_array(j) > pixel_array(j + 1)
				tmp = pixel_array(j)
				pixel_array(j) = pixel_array(j + 1)
				pixel_array(j + 1) = tmp
			ENDIF
		NEXT
	ENDIF
NEXT

If you want to limit this to a specific region of interest, I would mask the data so all pixels outside the ROI become negative before the Bubble Sort.  Then, to take the Top 100, this would simply be the last 100 elements in the sorted array and to get the Bottom 100, you would simple loop through the sorted array until you get the first non-negative number and count up 100 elements from there.


Matteo
  • Author
  • Monochrome
  • 3 replies
  • July 25, 2025

Hello both of you,
thank you very much for your help.
In retrospect a stupid error with the code, I was probably too close to it... 🤐

Thanks again!
Best,
Matteo


David.Nguyen
Luminary
Forum|alt.badge.img+2

I know this feeling all too well...


Reply


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings