Created
May 24, 2021 03:59
-
-
Save imabug/68792bb0a8480733e1ef1e805a086e60 to your computer and use it in GitHub Desktop.
AAPM Report 270 ImageJ macros
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This code will generate a modified TG18PQC pattern following the same basic modifications used in the pacsDisplay iQC pattern. The user should set up the size variable for the desired image size. Typically this is either a 1024x1024 image or 2048x2048 image. In this script, a square pattern is generated, though it can be of arbitrary size. The size of everything is based on the TG18PQC definitions and will just be scaled accordingly (though it may have some errors). | |
This version of the script will assume an 8-bit grayscale. | |
*/ | |
size = 1024; | |
newImage("TG270-pQC", "8-bit black", size, size, 1); | |
// Define some of the sizes (everything is normalized to 1024) | |
sidew = floor(size/1024*87); | |
barh = floor(size/1024*50); | |
toph = floor(size/1024*62); | |
// Create the horizontal bars and the contrast modulation within (varying contrast and frequency) | |
// 18 bars (0, 15, 30, ..., 240, 255) | |
run("Macro...", "code=[if(y>=" + toph + "&&y<" + size - toph + ") v=floor((y-" + toph + ")/" + barh + ")*15]"); | |
// Contrast modulation | |
// Horizontal bars | |
freqH = newArray(18, 12, 6, 4); | |
for (i = 0; i < 4; i++) { | |
f = freqH[i]; | |
// Left contrast (8) | |
run("Macro...", "code=[if(x>=" + sidew + i*barh + "&&x<" + sidew + barh + i*barh + "&&((y-" + toph + ")%" + f + "<" + f/2 + ")) v=v-4]"); | |
run("Macro...", "code=[if(x>=" + sidew + i*barh + "&&x<" + sidew + barh + i*barh + "&&((y-" + toph + ")%" + f + ">=" + f/2 + ")) v=v-4]"); | |
// Add additional four to the top row and subtract four from the bottom row | |
run("Macro...", "code=[if(x>=" + sidew + i*barh + "&&x<" + sidew + barh + i*barh + "&&y<" + toph + barh + "&&((y-" + toph + ")%" + f + ">=" + f/2 + ")) v=v+4]"); | |
run("Macro...", "code=[if(x>=" + sidew + i*barh + "&&x<" + sidew + barh + i*barh + "&&y>=" + size - toph - barh + "&&((y-" + toph + ")%" + f + "<" + f/2 + ")) v=v-4]"); | |
//Left contrast (2) | |
run("Macro...", "code=[if(x>=" + sidew + (i + 4)*barh + "&&x<" + sidew + barh + (i + 4)*barh + "&&((y-" + toph + ")%" + f + "<" + f/2 + ")) v=v-1]"); | |
run("Macro...", "code=[if(x>=" + sidew + (i + 4)*barh + "&&x<" + sidew + barh + (i + 4)*barh + "&&((y-" + toph + ")%" + f + ">=" + f/2 + ")) v=v+1]"); | |
// Add additional one to the top row and subtract one from the bottom row | |
run("Macro...", "code=[if(x>=" + sidew + (i + 4)*barh + "&&x<" + sidew + barh + (i + 4)*barh + "&&y<" + toph + barh + "&&((y-" + toph + ")%" + f + ">=" + f/2 + ")) v=v+1]"); | |
run("Macro...", "code=[if(x>=" + sidew + (i + 4)*barh + "&&x<" + sidew + barh + (i + 4)*barh + "&&y>=" + size - toph - barh + "&&((y-" + toph + ")%" + f + "<" + f/2 + ")) v=v-1]"); | |
} | |
// Vertical bars | |
freqV = newArray(4, 6, 12, 18); | |
for (i = 9; i < 13; i++) { | |
f = freqV[i - 9]; | |
// Right contrast (8) | |
run("Macro...", "code=[if(x>=" + sidew + (i + 4)*barh + "&&x<" + sidew + barh + (i + 4)*barh + "&&((x-" + sidew + i*barh + ")%" + f + "<" + f/2 + ")) v=v-4]"); | |
run("Macro...", "code=[if(x>=" + sidew + (i + 4)*barh + "&&x<" + sidew + barh + (i + 4)*barh + "&&((x-" + sidew + i*barh + ")%" + f + ">=" + f/2 + ")) v=v+4]"); | |
//Add additional four to the top row and subtract four from the bottom row | |
run("Macro...", "code=[if(x>=" + sidew + (i + 4)*barh + "&&x<" + sidew + barh + (i + 4)*barh + "&&y<" + toph + barh + "&&((x-" + sidew + i*barh + ")%" + f + ">=" + f/2 + ")) v=v+4]"); | |
run("Macro...", "code=[if(x>=" + sidew + (i + 4)*barh + "&&x<" + sidew + barh + (i + 4)*barh + "&&y>=" + size - toph - barh + "&&((x-" + sidew + i*barh + ")%" + f + "<" + f/2 + ")) v=v-4]"); | |
// Right contrast (2) | |
run("Macro...", "code=[if(x>=" + sidew + i*barh + "&&x<" + sidew + barh + i*barh + "&&((x-" + sidew + i*barh + ")%" + f + "<" + f/2 + ")) v=v-1]"); | |
run("Macro...", "code=[if(x>=" + sidew + i*barh + "&&x<" + sidew + barh + i*barh + "&&((x-" + sidew + i*barh + ")%" + f + ">=" + f/2 + ")) v=v+1]"); | |
// Add additional one to the top row and subtract one from the bottom row | |
run("Macro...", "code=[if(x>=" + sidew + i*barh + "&&x<" + sidew + barh + i*barh + "&&y<" + toph + barh + "&&((x-" + sidew + i*barh + ")%" + f + ">=" + f/2 + ")) v=v+1]"); | |
run("Macro...", "code=[if(x>=" + sidew + i*barh + "&&x<" + sidew + barh + i*barh + "&&y>=" + size - toph - barh + "&&((x-" + sidew + i*barh + ")%" + f + "<" + f/2 + ")) v=v-1]"); | |
} | |
// Horizontal bars at the top and bottom of the phantom with high-contrast line par phantoms | |
run("Macro...", "code=[if(y<" + toph + ") v=64]"); | |
run("Macro...", "code=[if(y>=" + size - toph + ") v=191]"); | |
// Line pairs | |
freqLP = newArray(6, 4, 2, 2, 4, 6); | |
topM = toph/2; | |
for (i = 0; i < 3; i++) { | |
f = freqLP[i]; | |
run("Macro...", "code=[if(x>=" + sidew + (2*i + 2)*barh + "&&x<" + sidew + barh + (2*i + 2)*barh + "&&y>" + topM - barh/2 + "&&y<=" + topM + barh/2 + "&&((y-" + (topM - barh/2) + ")%" + f + "<" + f/2 + ")) v=128]"); | |
run("Macro...", "code=[if(x>=" + sidew + (2*i + 2)*barh + "&&x<" + sidew + barh + (2*i + 2)*barh + "&&y>" + topM - barh/2 + "&&y<=" + topM + barh/2 + "&&((y-" + (topM - barh/2) + ")%" + f + ">=" + f/2 + ")) v=0]"); | |
run("Macro...", "code=[if(x>=" + sidew + (2*i + 2)*barh + "&&x<" + sidew + barh + (2*i + 2)*barh + "&&y>" + size - topM - barh/2 + "&&y<=" + size - topM + barh/2 + "&&((y-" + (topM - barh/2) + ")%" + f + "<" + f/2 + ")) v=128]"); | |
run("Macro...", "code=[if(x>=" + sidew + (2*i + 2)*barh + "&&x<" + sidew + barh + (2*i + 2)*barh + "&&y>" + size - topM - barh/2 + "&&y<=" + size - topM + barh/2 + "&&((y-" + (topM - barh/2) + ")%" + f + ">=" + f/2 + ")) v=255]"); | |
} | |
for (i = 3; i < 6; i++) { | |
f = freqLP[i]; | |
run("Macro...", "code=[if(x>=" + sidew + (2*i + 4)*barh + "&&x<" + sidew + barh + (2*i + 4)*barh + "&&y>" + topM - barh/2 + "&&y<=" + topM + barh/2 + "&&((x-" + sidew + (2*i + 4)*barh + ")%" + f + "<" + f/2 + ")) v=128]"); | |
run("Macro...", "code=[if(x>=" + sidew + (2*i + 4)*barh + "&&x<" + sidew + barh + (2*i + 4)*barh + "&&y>" + topM - barh/2 + "&&y<=" + topM + barh/2 + "&&((x-" + sidew + (2*i + 4)*barh + ")%" + f + ">=" + f/2 + ")) v=0]"); | |
run("Macro...", "code=[if(x>=" + sidew + (2*i + 4)*barh + "&&x<" + sidew + barh + (2*i + 4)*barh + "&&y>" + size - topM - barh/2 + "&&y<=" + size - topM + barh/2 + "&&((x-" + sidew + (2*i + 4)*barh + ")%" + f + "<" + f/2 + ")) v=128]"); | |
run("Macro...", "code=[if(x>=" + sidew + (2*i + 4)*barh + "&&x<" + sidew + barh + (2*i + 4)*barh + "&&y>" + size - topM - barh/2 + "&&y<=" + size - topM + barh/2 + "&&((x-" + sidew + (2*i + 4)*barh + ")%" + f + ">=" + f/2 + ")) v=255]"); | |
} | |
// Vertical side bars with modulating gradient | |
strip = sidew/2; | |
// Left side | |
run("Macro...", "code=[if(x<" + sidew + ") v=y/" + size - 1 + "*1031/1024*255]"); | |
run("Macro...", "code=[if(x<" + sidew/2 + (strip/2) + "&&x>" + sidew/2 - (strip/2) + ") v=v+3*sin(y/2)]"); | |
// Right side | |
run("Macro...", "code=[if(x>=" + size - sidew + ") v=-1*(y-" + size - 1 + ")/" + size - 1 + "*1031/1024*255]"); | |
run("Macro...", "code=[if(x<" + size - sidew/2 + (strip/2) + "&&x>" + size - sidew/2 - (strip/2) + ") v=v+3*sin(y/1.5)]"); | |
//Set up fonts for labeling | |
setFont("SansSerif", 18, "antialiased"); | |
setJustification("center"); | |
setColor(128, 128, 128); | |
drawString("TG270-pQC", size/2 - 1, 32); | |
setMetadata("Label", "TG270-pQC"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Use this code to generate the sQC (simple QC) TG270 pattern. | |
This version of the script will assume an 8-bit grayscale. | |
This pattern is intended to be used by non-physicists as a quick visual | |
evaluation of display performance. It contains three rows of six incrementing | |
grayscale squares (for a total of 18 squares) that cover from 0 to 255. Each | |
square contains a smaller square in the upper left and lower right corner. | |
These sub-squares contain a modulation pattern iwth a period of 6 p ixels and | |
50% duty cycle. The patterns use vertical bars that differ from the background | |
value of the square by 5 gray levels. The pattern in the upper left corner of | |
each square has vertical bars with a lower pixel value than the square, while | |
the pattern in the lower right has vertical bars with higher pixel values. For | |
the first square (GL 0), the pattern in the upper left cannot be lower than | |
the main square. Its bars have a contrast of 3 gray levels above the | |
background. Similarly, the last square (GL 255) cannot have a lower right | |
sub-square with higher pixel values, and so it has a modulation contrast of 3 | |
gray levels below the background. | |
For a DICOM-conformant display, the use should be able to quickly scan the | |
three rows of squares and assess if the modulation patterns in each are | |
equally visible across all 18 gray levels. The upper-leftmost and | |
lower-rightmost patterns correspond to the ones with the lower contrast (3 | |
gray levels vs. 5); these may only be visible on the highest-performance | |
displays. | |
The sQC pattern also includes three larger squares at the bottom of the | |
pattern that allow for minimum and maximum luminance calculations (and | |
therefore luminance ratio). These three patterns (black, mid-gray, white) can | |
be zoomed and panned around the display for bad pixel and uniformity | |
measurements. Furthermore, any of the squares in the three rows can be zoomed | |
and panned for similar use. The squares in the three rows correspond to the | |
gray levels used in the traditional 18-point TG18 luminance response | |
measurement. | |
Along the bottom of the pattern, a grayscale gradient is included with | |
continuous pixel value variation for evaluating bit depth issues, contouring | |
artifacts, and grayscale errors. At the side of the gradient, line pairs in | |
the horizontal and vertical direction can be used to verify correct pixel | |
mapping. | |
*/ | |
// Define the size of the image (1024 or 2048) | |
size = 1024; | |
// Based on the size, set a scaler value | |
sc = size / 1024; | |
//Create the image | |
newImage("TG270-sQC", "8-bit black", size, size, 1); | |
// Set the background | |
background = 128; | |
// Set the period of the bar patterns (pixels/lp) | |
barper = 6; | |
// Set the entire page to the background | |
run("Macro...", "code=v=" + background + ""); | |
// Set up fonts for labeling | |
setFont("SansSerif", 18, "antialiased"); | |
setJustification("center"); | |
color=background-75; | |
setColor(color, color, color); | |
drawString("TG270-sQC", size/2 - 1,32); | |
setMetadata("Label", "TG270-sQC"); | |
// Create a for loop to draw the boxes and contrast squares | |
ioff = 1; // Define i offset to move the group of boxes up/down | |
for (i = 0; i < 3; i++) { | |
for (j = 0; j < 6; j++) { | |
// Set the color based on the row, column (i, j) coordinates in steps of 15 | |
/* | |
The power command allows the grays to be read continuously by | |
switching the order of the second row. This way the eye can follow the | |
grays from left to right, move down, then go from right to left in the | |
second row before going left to right for the final row. | |
*/ | |
color = (i*6 + 2.5 - 2.5*pow(-1, i) + j*pow(-1, i))*15; | |
setColor(color, color, color); | |
// Draw the main squares (128x128 for 1024) | |
fillRect((sc*160*j + sc*48), sc*128*(i + ioff) + (i - 1)*sc*32, sc*128, sc*128); | |
// Label the squares (comment the drawString command to remove labels) | |
setFont("SansSerif", 12, "antialiased"); | |
setJustification("center"); | |
tcolor = (i*6 + 2.5 - 2.5*pow(-1, i) + j*pow(-1, i))%9*15 + 75; | |
setColor(tcolor, tcolor, tcolor); | |
// Label the squares with gray levels | |
drawString((i*6 + 2.5 - 2.5*pow(-1, i) + j*pow(-1, i))*15, (sc*160*j + sc*48) + sc*64, sc*128*(i + ioff) + (i-1)*sc*32 + sc*16); | |
// Label the squares with indices | |
// drawString((i*6 + 2.5 - 2.5*pow(-1, i) + j*pow(-1, i))*15, (sc*160*j + sc*48) + sc*64, sc*128*(i + ioff) + (i-1)*sc*32 + sc*16); | |
// Set the contrast color to 2% of the main square and draw the modulation pattern | |
// Create special conditions for the first and last box | |
if ((i == 0) && (j == 0)) { | |
for (k = 0; k < ((32/barper)*sc); k++) { | |
lowc = 3; | |
setColor(lowc, lowc, lowc); | |
fillRect((sc*160*j + sc*48) + sc*16 + k*barper, sc*128*(i + ioff) + (i - 1)*sc*32 + sc*16, barper/2, sc*32); | |
cont = 5; | |
setColor(cont, cont, cont); | |
fillRect((sc*160*j + sc*48) + sc*80 + k*barper, sc*128*(i + ioff) + (i - 1)*sc*32 + sc*80, barper/2, sc*32); | |
} | |
} else { | |
if ((i == 2) && (j == 5)) { | |
for (k = 0; k < (32/barper)*sc; k++) { | |
lowc = (i*6 + 2.5 - 2.5*pow(-1, i) + j*pow(-1, i))*15 - 3; | |
setColor(lowc, lowc, lowc); | |
fillRect((sc*160*j + sc*48) + sc*80 + k*barper, sc*128*(i + ioff) + (i - 1)*sc*32 + sc*80, barper/2, sc*32); | |
cont = (i*6 + 2.5 - 2.5*pow(-1, i) + j*pow(-1, i))*15 - 5; | |
setColor(cont, cont, cont); | |
fillRect((sc*160*j + sc*48) + sc*16 + k*barper, sc*128*(i + ioff) + (i - 1)*sc*32 + sc*16, barper/2, sc*32); | |
} | |
} else { | |
for (k = 0; k < (32/barper)*sc; k++) { | |
cont = (i*6 + 2.5 - 2.5*pow(-1, i) + j*pow(-1, i))*15 - 5; | |
setColor(cont, cont, cont); | |
fillRect((sc*160*j + sc*48) + sc*16 + k*barper, sc*128*(i + ioff) + (i - 1)*sc*32 + sc*16, barper/2, sc*32); | |
cont = (i*6 + 2.5 - 2.5*pow(-1, i) + j*pow(-1, i))*15 + 5; | |
setColor(cont, cont, cont); | |
fillRect((sc*160*j + sc*48) + sc*80 + k*barper, sc*128*(i + ioff) + (i - 1)*sc*32 + sc*80, barper/2, sc*32); | |
} | |
} | |
} | |
} | |
} | |
// Draw some big squares along the bottom for bad pixel testing and luminance measuring | |
koff = 4; | |
for (k = 0; k < 3; k++) { | |
color = k*128; // Set the color | |
setColor(color, color, color); | |
fillRect(sc*(128 + k*256), sc*128*(koff + 1), sc*256, sc*256); // Draw the main squares (128x128 for 1024) | |
} | |
// Draw vertical gradient along the right side: | |
// run("Macro...", "code=[if(x>"+sc*(size-64)+"&&y<="+size-sc*64+"&&y>="+sc*64+") v=(y-"+sc*64+")/"+size-sc*64*2+"*255]"); | |
// Draw horizontal gradient along the bottom (either with or without line patterns) | |
// With line patterns at the sides | |
run("Macro...","code=[if(y>"+(size-sc*64)+"&&x<="+size-2*sc*64+") v=(x-"+sc*64+")/"+size-sc*64*2+"*255]"); | |
run("Macro...","code=[if(y>"+(size-sc*64)+"&&x>"+size-2*sc*64+"&&x<"+size-sc*64+") v=(x%2)*255]"); | |
run("Macro...","code=[if(y>"+(size-sc*64)+"&&x>"+size-sc*64+") v=(y%2)*255]"); | |
// Without the line patterns | |
// run("Macro...", "code=[if(y>" + sc∗(size − 64) + ") v=(x−" + sc∗64 + ")/" + size − sc∗64∗2 + "∗255]") ; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Use this code to generate a series of temporal resolution images for an animated gif | |
// Define the size of the frame | |
vsize = 1024; | |
hsize = 1024; | |
// Define a scalar to adjust from the default size | |
vsc = vsize/1024; | |
hsc = hsize/1024; | |
// Define the number of rows (powers of 2 work best) | |
rows = 16; | |
// Define the size of the blocks (assumes square size) | |
block = 32; | |
// Define the number of blocks (4-7 recommended) | |
numbl = 6; | |
// Define the number of frames (based on block count) | |
frames = hsize/(2*block) + 2 + (numbl - 1); | |
// Define frame rate | |
fps = 60; | |
// Create the image stack | |
newImage("TG270-TR2", "black", hsize, vsize, frames); | |
// Set the gray level pairs. 2*rows pairs are required. | |
gl = newArray(015, 020, 015,030, 015, 045, 015, 065, 120, 125, 120, 135, 120, 150, 120, 170, 245, 240, 245, 230, 245, 215, 245, 195, 000, 075, 000, 255, 255, 180, 255, 000); | |
setFont("SansSerif", 10, "antialiased"); | |
setJustification("center"); | |
// a = vsize/rows; | |
// b = vsc*block; | |
// c = 2*block*hsc; | |
// d = hsize - (hsize - hsc*2*numbl*block); | |
// e = block*hsc; | |
// f = 0.5*(vsize/rows - vsc*block; | |
// Create the test pattern background. Loop through each gray level pair base. | |
for (i = 0; i < rows; i++) { | |
setColor(gl[2*i], gl[2*i], gl[2*i]); | |
run("Macro...", "code=[if(y>=" + i*vsize/rows + "&&y<=" + (i + 1)*vsize/rows + ") v=" + gl[2*i] + "] stack"); | |
run("Macro...", "code=[if(y>=" + i*vsize/rows + 0.5*(vsize/rows - vsc*block) + "&&y<=" + (i + 1)*vsize/rows - 0.5*(vsize/rows - vsc*block) + "&&x>=" + 2*block*hsc + "*z-" + hsize - (hsize - hsc*2*numbl*block) + "&&x<" + 2*block*hsc + "*z&&x%" + 2*block*hsc + ">" + block*hsc - 1 + ") v=" + gl[2*i] + " + (" + gl[2*i + 1] + "-" + gl[2*i]+ ")] stack"); | |
//set text color | |
tcolor = gl[2*i] + (64*pow(-1, floor(gl[2*i]/128))); | |
setColor(tcolor, tcolor, tcolor); | |
//set up labels | |
for (j = 0; j < frames; j ++) { | |
setSlice(j + 1); | |
// Label each frame | |
drawString(j, j*hsc*block*2 - (numbl - 0.5)*block*hsc, i*vsize/rows + 0.5*(vsize/rows - vsc*block)); | |
// Label each pair | |
drawString(gl[2*i] + "/" + gl[2*i + 1], hsize/2, i*vsize/rows + (vsize/rows - vsc*block) + vsc*block); | |
} | |
} | |
// Set up the animation options, set to desired fps | |
run("Animation Options...", "speed=" + fps + " first=1 last=" + frames + " start"); | |
// If this command is enabled, just select the option to use the slice labels as the file names. | |
// run("Image sequence...",); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Use this code to generate combination UN and LN test patterns (TG270-ULN) | |
// Define the number of images (18 (15 GL), 52 (5 GL), 86 (3 GL) or 256 (1 GL)) | |
images = 256; | |
// Define the size of the images (1024 or 2048) | |
size = 1024; | |
// Create the image stack (Re-define the image type for other bit-depths) | |
newImage("TG270-ULN8-", "8-bit black", size, size, images); | |
// Set the image values and draw the appropriate lines | |
step = 255/(images - 1); | |
// Set the entire page to the z*step value | |
run("Macro...", "code=[v=z*" + step + "] stack"); | |
// Set up fonts for labeling | |
setFont("SansSerif", 18, "antialiased"); | |
setJustification("center"); | |
// Create a for loop to label the images and draw the grid liens | |
for (i = 0; i < images; i++) { | |
setSlice(i + 1); | |
color = (i%(images/2) + images/6)*step; | |
setColor(color, color, color); | |
drawString("TG270-ULN8-" + IJ.pad(i*step, 3) + "", size/2 - 1, 32); | |
setMetadata("Label", "TG270-ULN8-" + IJ.pad(i*step, 3) + ""); | |
drawRect(340*size/1024, 0, 2, size); | |
drawRect(682*size/1024, 0, 2, size); | |
drawRect(0, 340*size/1024, size, 2); | |
drawRect(0, 682*size/1024, size, 2); | |
} | |
//If this command is enabled, just select the option to use the slice labels as the file names | |
// run("Image Sequence...",); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment