问题
def normalize_brightness(img: Image) -> Image: """
Normalize the brightness of the given Image img by:
computing the average brightness of the picture: - this can be done by calculating the average brightness of each pixel in img (the average brightness of each pixel is the sum of the values of red, blue and green of the pixel, divided by 3 as a float division) - the average brightness of the picture is then the sum of all the pixel averages, divided by the product of the width and height of img
find the factor, let's call it x, which we can multiply the average brightness by to get the value of 128.
multiply the colors in each pixel by this factor x """
img_width, img_height = img.size pixels = img.load() # create the pixel map h = 0 for i in range(img_width): for j in range(img_height): r, g, b = pixels[i, j] avg = sum(pixels[i, j]) / 3 h += avg total_avg = int(h / (img_width * img_height)) x = 128 // total_avg r, g, b = pixels[i, j] pixels[i, j] = (r * x, g * x, b * x) return img
I am a little lost as to what I am doing wrong can someone help?
回答1:
You really should avoid for
loops when image processing with Python whenever possible because it is seriously slow, verbose, harder to read and more likely to contain errors. Try to use vectorised Numpy functions, or OpenCV or PIL built-in functions.
#!/usr/bin/env python3
from PIL import Image
import numpy as np
def normalize(im):
"""Normalise brightness of image"""
# Convert to Numpy array
na = np.array(im, dtype=np.float32)
# Calculate average brightness
avg = na.mean()
# Calculate factor x
x = 128 / avg
# Scale whole array as float since likely fractional
na *= x
# Convert back to PIL Image and return
return Image.fromarray(na.astype(np.uint8))
# Load image and normalize
im = Image.open('start.png').convert('RGB')
result = normalize(im)
result.save('result.png')
This code runs in around 800 microseconds on my machine whereas any version with a for
loop requires around 70x longer.
Input image:
Result:
回答2:
Your calculation code to get the factor seems okay, processing every pixel to get the average of sum of averages.
However, your modification code to adjust the brightness is not done within a similar loop so it will operate on one pixel, and I'm not even sure that pixel is even within the image. You should do that within a loop as well:
for i in range(img_width):
for j in range(img_height):
(r, g, b) = pixels[i, j]
pixels[i, j] = (r * x, g * x, b * x)
This should replace the third-last and second-last lines of what you have at the moment (between x = ...
and return ...
). So what you would end up with is:
img_width, img_height = img.size
pixels = img.load() # create the pixel map
h = 0
for i in range(img_width):
for j in range(img_height):
r, g, b = pixels[i, j]
avg = sum(pixels[i, j]) / 3
h += avg
total_avg = int(h / (img_width * img_height))
x = 128 // total_avg
# == New stuff below
for i in range(img_width):
for j in range(img_height):
(r, g, b) = pixels[i, j]
pixels[i, j] = (r * x, g * x, b * x)
# == New stuff above
return img
A few other things to look in to:
First, I'm not sure if returning img
is the right thing to do here, unless pixels
is a reference to (not copy of) the pixels in the image. You may want to check up on that as well.
Further, it may be possible that the value for [rgb] * x
gives you something more than 255 for certain input data sets. If that's the case, you probably want to clamp them into the range 0..255
to ensure this doesn't happen. Something like (replacing the "new stuff" in the code above):
for i in range(img_width):
for j in range(img_height):
# Get original pixel.
(r, g, b) = pixels[i, j]
# Scale with upper limit.
r = min(255, r * x)
g = min(255, g * x)
b = min(255, b * x)
# Replace pixel with scaled one.
pixels[i, j] = (r, g, b)
回答3:
At first, thanks to paxdiablo for sharing his answer.
I would just like to improve on the answer.
The calculation of the average can be optimized using list comprehension like:
x = 128 // (sum([sum(pixels[i, j]) / 3 for i in range(img_width) for j in range(img_height)]) / (img_width * img_height))
So my complete answer will be:
Normalize the brightness of the given Image
img_width, img_height = img.size
pixels = img.load() # create the pixel map
x = 128 // (sum([sum(pixels[i, j]) / 3 for i in range(img_width) for j in range(img_height)]) / (img_width * img_height))
for i in range(img_width):
for j in range(img_height):
r, g, b = pixels[i, j]
pixels[i, j] = [min(255, r * x), min(255, g * x), min(255, b * x)]
return img
来源:https://stackoverflow.com/questions/60925663/rgb-values-and-pixels