Skip to content

Commit c6f9186

Browse files
committed
2025.08.28 (1.54q21; ContrastAdjuster)
1 parent 8360e9a commit c6f9186

File tree

3 files changed

+125
-28
lines changed

3 files changed

+125
-28
lines changed

ij/ImageJ.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public class ImageJ extends Frame implements ActionListener,
7979

8080
/** Plugins should call IJ.getVersion() or IJ.getFullVersion() to get the version string. */
8181
public static final String VERSION = "1.54q";
82-
public static final String BUILD = "19";
82+
public static final String BUILD = "21";
8383
public static Color backgroundColor = new Color(237,237,237);
8484
/** SansSerif, 12-point, plain font. */
8585
public static final Font SansSerif12 = new Font("SansSerif", Font.PLAIN, 12);

ij/plugin/frame/ContrastAdjuster.java

Lines changed: 118 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
2222
static final int AUTO_THRESHOLD = 5000;
2323
static final String[] channelLabels = {"Red", "Green", "Blue", "Cyan", "Magenta", "Yellow", "All"};
2424
static final String[] altChannelLabels = {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "All"};
25+
static final String[] greyChannelLabels = {"LUT level"};
2526
static final int[] channelConstants = {4, 2, 1, 3, 5, 6, 7};
2627

2728
ContrastPlot plot = new ContrastPlot();
@@ -55,6 +56,8 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
5556
Font sanFont = IJ.font12;
5657
int channels = 7; // RGB
5758
Choice choice;
59+
Checkbox logHistCheckbox;
60+
boolean isLogHist = false;
5861
private String blankLabel8 = "--------";
5962
private String blankLabel12 = "------------";
6063
private double scale = Prefs.getGuiScale();
@@ -127,6 +130,14 @@ else if (balance) {
127130
blankLabel8 = " ";
128131
}
129132

133+
// log histogram scale checkbox
134+
logHistCheckbox = new Checkbox("Log scale");
135+
logHistCheckbox.setState(isLogHist);
136+
logHistCheckbox.addItemListener(this);
137+
c.gridy = y++;
138+
gridbag.setConstraints(logHistCheckbox, c);
139+
add(logHistCheckbox);
140+
130141
// min slider
131142
if (!windowLevel) {
132143
minSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/2, 1, 0, sliderRange);
@@ -256,6 +267,9 @@ void addBalanceChoices() {
256267
if (imp!=null && imp.isComposite()) {
257268
for (int i=0; i<altChannelLabels.length; i++)
258269
choice.addItem(altChannelLabels[i]);
270+
} else if (imp!=null && ((imp.getType() == ImagePlus.GRAY8) || (imp.getType() == ImagePlus.GRAY16) || (imp.getType() == ImagePlus.GRAY32))) {
271+
for (int i=0; i<greyChannelLabels.length; i++)
272+
choice.addItem(greyChannelLabels[i]);
259273
} else {
260274
for (int i=0; i<channelLabels.length; i++)
261275
choice.addItem(channelLabels[i]);
@@ -387,15 +401,22 @@ else if (newSliderRange>=1280)
387401
if (imp.isComposite()) {
388402
int channel = imp.getChannel();
389403
if (channel<=4) {
404+
choice.removeAll();
405+
addBalanceChoices();
390406
choice.select(channel-1);
391407
channels = channelConstants[channel-1];
392408
}
393-
if (choice.getItem(0).equals("Red")) {
409+
if (!choice.getItem(0).equals("Channel 1")) { // if the choice is wrong
410+
choice.removeAll();
411+
addBalanceChoices();
412+
}
413+
} else if ((imp.getType() == ImagePlus.GRAY8) || (imp.getType() == ImagePlus.GRAY16) || (imp.getType() == ImagePlus.GRAY32)) { // grey image
414+
if (!choice.getItem(0).equals("LUT level")) { // if the choice is wrong
394415
choice.removeAll();
395416
addBalanceChoices();
396417
}
397418
} else { // not composite
398-
if (choice.getItem(0).equals("Channel 1")) {
419+
if (!choice.getItem(0).equals("Red")) { // if the choice is wrong
399420
choice.removeAll();
400421
addBalanceChoices();
401422
}
@@ -607,6 +628,7 @@ void reset(ImagePlus imp, ImageProcessor ip) {
607628
void plotHistogram(ImagePlus imp) {
608629
ImageStatistics stats;
609630
if (balance && (channels==4 || channels==2 || channels==1) && imp.getType()==ImagePlus.COLOR_RGB) {
631+
setTitle("Color");
610632
int w = imp.getWidth();
611633
int h = imp.getHeight();
612634
byte[] r = new byte[w*h];
@@ -623,17 +645,19 @@ else if (channels==1)
623645
ImageProcessor ip = new ByteProcessor(w, h, pixels, null);
624646
stats = ImageStatistics.getStatistics(ip, 0, imp.getCalibration());
625647
} else {
648+
if (balance) {setTitle("Color");}
649+
if (balance && ((imp.getType() == ImagePlus.GRAY8) || (imp.getType() == ImagePlus.GRAY16) || (imp.getType() == ImagePlus.GRAY32)) && !imp.isComposite()) { // image is grey
650+
setTitle("LUT Color");
651+
}
626652
int range = imp.getType()==ImagePlus.GRAY16?ImagePlus.getDefault16bitRange():0;
627653
if (range!=0 && imp.getProcessor().getMax()==Math.pow(2,range)-1 && !(imp.getCalibration().isSigned16Bit())) {
628654
ImagePlus imp2 = new ImagePlus("Temp", imp.getProcessor());
629655
stats = new StackStatistics(imp2, 256, 0, Math.pow(2,range));
630656
} else
631657
stats = imp.getStatistics();
632658
}
633-
Color color = Color.gray;
634-
if (imp.isComposite() && !(balance&&channels==7))
635-
color = ((CompositeImage)imp).getChannelColor();
636-
plot.setHistogram(stats, color);
659+
// Default histogram color for images without LUT is now defined in the setHistogram method
660+
plot.setHistogram(stats, isLogHist);
637661
}
638662

639663
void apply(ImagePlus imp, ImageProcessor ip) {
@@ -1146,6 +1170,7 @@ void doUpdate() {
11461170
case BRIGHTNESS: adjustBrightness(imp, ip, bvalue); break;
11471171
case CONTRAST: adjustContrast(imp, ip, cvalue); break;
11481172
}
1173+
plotHistogram(imp);
11491174
updatePlot();
11501175
updateLabels(imp);
11511176
if ((IJ.shiftKeyDown()||(balance&&channels==7)) && imp.isComposite())
@@ -1186,19 +1211,35 @@ public void windowActivated(WindowEvent e) {
11861211
}
11871212

11881213
public synchronized void itemStateChanged(ItemEvent e) {
1189-
int index = choice.getSelectedIndex();
1190-
channels = channelConstants[index];
1191-
ImagePlus imp = WindowManager.getCurrentImage();
1192-
if (imp!=null && imp.isComposite()) {
1193-
if (index+1<=imp.getNChannels())
1194-
imp.setPosition(index+1, imp.getSlice(), imp.getFrame());
1195-
else {
1196-
choice.select(channelLabels.length-1);
1197-
channels = 7;
1214+
Object source = e.getSource();
1215+
if (source==logHistCheckbox) {
1216+
isLogHist=logHistCheckbox.getState();
1217+
// IJ.log("log state changed");
1218+
ImagePlus imp = WindowManager.getCurrentImage();
1219+
if (imp!=null) {
1220+
// IJ.log("now updating histogram from itemStateChanged "+imp);
1221+
plotHistogram(imp);
1222+
updatePlot();
1223+
updateLabels(imp);
1224+
}
1225+
}
1226+
else {
1227+
int index = choice.getSelectedIndex();
1228+
channels = channelConstants[index];
1229+
ImagePlus imp = WindowManager.getCurrentImage();
1230+
if (imp!=null) {
1231+
if (imp.isComposite()) {
1232+
if (index+1<=imp.getNChannels())
1233+
imp.setPosition(index+1, imp.getSlice(), imp.getFrame());
1234+
else {
1235+
choice.select(channelLabels.length-1);
1236+
channels = 7;
1237+
}
1238+
} else {
1239+
imp.getProcessor().snapshot();
1240+
doReset = true;
1241+
}
11981242
}
1199-
} else {
1200-
imp.getProcessor().snapshot();
1201-
doReset = true;
12021243
}
12031244
notify();
12041245
}
@@ -1221,7 +1262,7 @@ public static void update() {
12211262

12221263

12231264
class ContrastPlot extends Canvas implements MouseListener {
1224-
1265+
Color[] hColors;
12251266
static final int WIDTH=128, HEIGHT=64;
12261267
double defaultMin = 0;
12271268
double defaultMax = 255;
@@ -1238,6 +1279,8 @@ class ContrastPlot extends Canvas implements MouseListener {
12381279

12391280
public ContrastPlot() {
12401281
addMouseListener(this);
1282+
width = (int)(width*1.3); // increase size
1283+
height = (int)(height*1.3); // increase size
12411284
if (scale>1.0) {
12421285
width = (int)(width*scale);
12431286
height = (int)(height*scale);
@@ -1251,9 +1294,36 @@ public Dimension getPreferredSize() {
12511294
return new Dimension(width+1, height+1);
12521295
}
12531296

1254-
void setHistogram(ImageStatistics stats, Color color) {
1255-
this.color = color;
1297+
void setHistogram(ImageStatistics stats, boolean isLogHist) {
12561298
histogram = stats.histogram;
1299+
if (isLogHist) {
1300+
for (int j=0;j<256;j++) {
1301+
histogram[j]=(int)(Math.log(histogram[j])*100);
1302+
}
1303+
}
1304+
ImagePlus imp = WindowManager.getCurrentImage();
1305+
hColors = new Color[256];
1306+
for (int i=0; i<256; i++) { // set the default histogram color when there is no LUT
1307+
hColors[i] = new Color(110, 110,150);
1308+
}
1309+
int impType= imp.getType();
1310+
if ((impType == ImagePlus.GRAY8) || (impType == ImagePlus.GRAY16) || (impType == ImagePlus.GRAY32)) { //if image has LUT
1311+
ImageProcessor ip = imp.getProcessor();
1312+
ColorModel cm = ip.getColorModel();
1313+
IndexColorModel icm = (IndexColorModel)cm;
1314+
int mapSize = icm.getMapSize();
1315+
if (mapSize!=256)
1316+
return;
1317+
byte[] red = new byte[256];
1318+
byte[] green = new byte[256];
1319+
byte[] blue = new byte[256];
1320+
icm.getReds(red);
1321+
icm.getGreens(green);
1322+
icm.getBlues(blue);
1323+
for (int i=0; i<256; i++) {
1324+
hColors[i] = new Color(red[i]&255, green[i]&255, blue[i]&255);
1325+
}
1326+
}
12571327
if (histogram.length!=256) {
12581328
histogram=null;
12591329
return;
@@ -1271,11 +1341,16 @@ void setHistogram(ImageStatistics stats, Color color) {
12711341
if ((histogram[i]>maxCount2) && (i!=mode))
12721342
maxCount2 = histogram[i];
12731343
}
1274-
hmax = stats.maxCount;
1344+
if (isLogHist) {
1345+
hmax =(int)(Math.log(stats.maxCount)*100);
1346+
} else {
1347+
hmax = stats.maxCount;
1348+
}
12751349
if ((hmax>(maxCount2*2)) && (maxCount2!=0)) {
12761350
hmax = (int)(maxCount2*1.5);
12771351
histogram[mode] = hmax;
12781352
}
1353+
12791354
os = null;
12801355
}
12811356

@@ -1284,9 +1359,17 @@ public void update(Graphics g) {
12841359
}
12851360

12861361
public void paint(Graphics g) {
1287-
int x1, y1, x2, y2;
1362+
int x1, y1, x2, y2,j,j1,j2;
1363+
double colscale;
12881364
double scale = (double)width/(defaultMax-defaultMin);
12891365
double slope = 0.0;
1366+
j1=(int)((min-defaultMin)/(defaultMax-defaultMin)*255);
1367+
j2=(int)((max-defaultMin)/(defaultMax-defaultMin)*255);
1368+
if (j2>j1) {
1369+
colscale=255.0/(j2-j1);
1370+
} else {
1371+
colscale=1;
1372+
}
12901373
if (max!=min)
12911374
slope = height/(max-min);
12921375
if (min>=defaultMin) {
@@ -1315,11 +1398,20 @@ public void paint(Graphics g) {
13151398
osg = os.getGraphics();
13161399
osg.setColor(Color.white);
13171400
osg.fillRect(0, 0, width, height);
1318-
osg.setColor(color);
13191401
double scale2 = width/256.0;
13201402
for (int i = 0; i < 256; i++) {
13211403
int x =(int)(i*scale2);
1322-
osg.drawLine(x, height, x, height - ((int)(height*histogram[i])/hmax));
1404+
j=(int) ((i-j1)*colscale);
1405+
if (i<j1) {j=0;};
1406+
if (i>j2) {j=255;};
1407+
// IJ.log("--> "+String.valueOf(j1)+" "+String.valueOf(j2)+" "+String.valueOf(i)+" "+String.valueOf(j));
1408+
if (hColors!=null)
1409+
osg.setColor(hColors[j]);
1410+
int y = height - ((int)(height*histogram[i])/hmax);
1411+
osg.drawLine(x, height, x, y);
1412+
osg.setColor(Color.black);
1413+
osg.fillRect(x, y, 1, 1);
1414+
//IJ.log("--> "+String.valueOf(i)+" "+String.valueOf(x)+" "+String.valueOf(histogram[i])+" "+String.valueOf(height - ((int)(height*histogram[i])/hmax)));
13231415
}
13241416
osg.dispose();
13251417
}
@@ -1360,3 +1452,4 @@ public Dimension getPreferredSize() {
13601452
} // TrimmedLabel class
13611453

13621454

1455+

release-notes.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
</head>
66
<body>
77

8-
9-
<li> <u>1.54q19 21 August 2025</u>
8+
<li> <u>1.54q21 28 August 2025</u>
109
<ul>
10+
<li> Thanks to 'AFMiJ', updated <i>Image&gt;Adjust&gt;Brightness/Contrast</i>
11+
to show the image histogram with the LUT colors of the image
12+
(<a href="https://github.com/imagej/ImageJ/issues/281#issuecomment-3219377578">Github</a>).
1113
<li> Thanks to 'aschain', the 3D filters in the <i>Process&gt;Filters</i>
1214
menu now work with hyperstacks.
1315
<li> Thanks to Michael Schmid, added 'auto' checkboxes to the
@@ -18,6 +20,8 @@
1820
<li> Removed the particle analyzers's "Record starts" option because
1921
it caused a NullPointerException when running in Fiji headless
2022
mode.
23+
<li> Thanks to Michael Schmid, histogram plots now show pixel values
24+
occurring once (count=1) in log mode.
2125
<li> Thanks to Michael Schmid, fixed bug where <i>Image&gt;Stacks&gt;Label</i>
2226
produces a NullPointerException in "overlay" mode when previewing
2327
and the current stack slice is not the first one.

0 commit comments

Comments
 (0)