Reputation: 851
the only function I can find is : gimp-color-balance, which takes the applicable parameters : preserve-lum(osity), cyan-red, magenta-green, and yellow-blue.
I'm not sure what values to pass for these parameters to duplicate the menu option in the title.
Upvotes: 8
Views: 4186
Reputation: 305
I use this function to auto white balance images. Unlike Gimp function, it does not normalize image contrast. So it is useful with low contrast images too.
import numpy as np
from imageio import imread
import matplotlib.pyplot as plt
def auto_white_balance(im, p=.6):
'''Stretch each channel histogram to same percentile as mean.'''
# get mean values
p0, p1 = np.percentile(im, p), np.percentile(im, 100-p)
for i in range(3):
ch = im[:,:,i]
# get channel values
pc0, pc1 = np.percentile(ch, p), np.percentile(ch, 100-p)
# stretch channel to same range as mean
ch = (p1 - p0) * (ch - pc0) / (pc1 - pc0) + p0
im[:,:,i] = ch
return im
def test():
im = imread('imageio:astronaut.png')
# distort white balance
im[:,:,0] = im[:,:,0] *.6
im[:,:,1] = im[:,:,1] *.8
plt.imshow(im)
plt.show()
im2 = auto_white_balance(im)
im2 = np.clip(im2, 0, 255) # or 0, 1 for float images
plt.imshow(im2)
plt.show()
if __name__ == "__main__":
test()
If you want equivalent of Gimp function, use fixed values instead: p0, p1 = 0, 255
Upvotes: 0
Reputation: 52777
Colors --> Auto --> White Balance
feature:Tested on Ubuntu 20.04.
Download the below code from my eRCaGuy_hello_world repo here: python/auto_white_balance_img.py.
Install dependencies:
pip3 install opencv-python # for cv2
pip3 install numpy
Now here is some fully-functional code, unlike some of the other answers here which are snippets and lacking things like import
statements. I'm borrowing from @Canette Ouverture's answer here, and @banderlog013's answer here.
Create file auto_white_balance_img.py:
#!/usr/bin/python3
import cv2
import numpy as np
file_in = 'test.jpg'
file_in_base = file_in[:-4] # strip file extension
file_in_extension = file_in[-4:]
img = cv2.imread(file_in)
# From @banderlog013's answer: https://stackoverflow.com/a/54864315/4561887
x = []
# get histogram for each channel
for i in cv2.split(img):
hist, bins = np.histogram(i, 256, (0, 256))
# discard colors at each end of the histogram which are used by only 0.05%
img_out1 = np.where(hist > hist.sum() * 0.0005)[0]
i_min = img_out1.min()
i_max = img_out1.max()
# stretch hist
img_out1 = (i.astype(np.int32) - i_min) / (i_max - i_min) * 255
img_out1 = np.clip(img_out1, 0, 255)
x.append(img_out1.astype(np.uint8))
# From @Canette Ouverture's answer: https://stackoverflow.com/a/56365560/4561887
img_out2 = np.zeros_like(img) # Initialize final image
for channel_index in range(3):
hist, bins = np.histogram(img[..., channel_index].ravel(), 256, (0, 256))
bmin = np.min(np.where(hist>(hist.sum()*0.0005)))
bmax = np.max(np.where(hist>(hist.sum()*0.0005)))
img_out2[...,channel_index] = np.clip(img[...,channel_index], bmin, bmax)
img_out2[...,channel_index] = ((img_out2[...,channel_index]-bmin) /
(bmax - bmin) * 255)
# Write new files
cv2.imwrite(file_in_base + '_out1' + file_in_extension, img_out1)
cv2.imwrite(file_in_base + '_out2' + file_in_extension, img_out2)
Make auto_white_balance_img.py executable:
chmod +x auto_white_balance_img.py
Now set the file_in
variable in the file above to your desired input image path, then run it with:
python3 auto_white_balance_img.py
# OR
./auto_white_balance_img.py
Assuming you have set file_in = 'test.jpg'
, it will produce these two files:
test_out1.jpg
# The result from @banderlog013's answer heretest_out2.jpg
# The result from @Canette Ouverture's answer hereUpvotes: 0
Reputation: 2505
According to GIMP doc, we need to discard pixel colors at each end of the Red, Green and Blue histograms which are used by only 0.05% of the pixels in the image and stretch the remaining range as much as possible (Python code):
import numpy as np
import cv2 # opencv-python
import matplotlib.pyplot as plt
img = cv2.imread('test.jpg')
x = []
# get histogram for each channel
for i in cv2.split(img):
hist, bins = np.histogram(i, 256, (0, 256))
# discard colors at each end of the histogram which are used by only 0.05%
tmp = np.where(hist > hist.sum() * 0.0005)[0]
i_min = tmp.min()
i_max = tmp.max()
# stretch hist
tmp = (i.astype(np.int32) - i_min) / (i_max - i_min) * 255
tmp = np.clip(tmp, 0, 255)
x.append(tmp.astype(np.uint8))
# combine image back and show it
s = np.dstack(x)
plt.imshow(s[::,::,::-1])
The result is pretty the same as after GIMP's 'Colors -> Auto -> White Balance'
UPD: we need np.clip()
because OpenCV
and numpy
differently casts int32 to uint8:
# Numpy
np.array([-10, 260]).astype(np.uint8)
>>> array([246, 4], dtype=uint8)
# but we need just [0, 255]
Upvotes: 2
Reputation: 56
To complete the answer of @banderlog013, I think the Gimp Doc specifies that the end pixels of each channel are first discarded, then the remaining ranges are stretched. I believe the right code is :
img = cv2.imread('test.jpg')
balanced_img = np.zeros_like(img) #Initialize final image
for i in range(3): #i stands for the channel index
hist, bins = np.histogram(img[..., i].ravel(), 256, (0, 256))
bmin = np.min(np.where(hist>(hist.sum()*0.0005)))
bmax = np.max(np.where(hist>(hist.sum()*0.0005)))
balanced_img[...,i] = np.clip(img[...,i], bmin, bmax)
balanced_img[...,i] = (balanced_img[...,i]-bmin) / (bmax - bmin) * 255
I obtain good results with it, try it out !
Upvotes: 4
Reputation: 2147
K, cool. Figured out how to script one up.
Use it if you like. Does alright by me.
https://github.com/doyousketch2/eAWB
Upvotes: -2
Reputation: 8904
From what I understand after a quick look at the source code (and more or less confirmed with a test image), these are unrelated and under the hood,Colors>Auto>White Balance
:
Proof with a synthetic image:
Before:
After:
All this isn't hard to do in Python.
Upvotes: 0