在Python / PIL中如何实现ImageMagick的“-level”?

5 投票
4 回答
5292 浏览
提问于 2025-04-16 00:21

我想在Python中调整一张图片的颜色级别。我可以使用任何在我的Ubuntu桌面上容易安装的Python库。我想做的事情和ImageMagick的-level功能一样(http://www.imagemagick.org/www/command-line-options.html#level)。不过,PIL(Python图像库)好像没有这个功能。我一直在对图片使用convert命令,然后再把文件读回来,但这样感觉有点浪费。有没有更好或者更快的方法呢?

4 个回答

2

这是我使用的代码。调整亮度的步骤有两个:1)在HSV图像的亮度通道上进行调整,2)根据结果中希望有多少黑色和白色的像素来进行调整。

这段代码可以修改,不使用Pillow库,因为OpenCV内部使用的是numpy数组。如果这样做,要注意OpenCV的原生颜色空间是BGR,所以你需要相应地更改调用cv.cvtColor()的部分。

from PIL import Image
import numpy as np
import cv2 as cv

fileName = 'foo.JPG'
fileOut = 'bar.JPG'
imgPil = Image.open(fileName) 
imgCV = np.asarray(imgPil, np.uint8)
hsv = cv.cvtColor(imgCV, cv.COLOR_RGB2HSV)
h,s,v = cv.split(hsv)
ceil = np.percentile(v,95) # 5% of pixels will be white
floor = np.percentile(v,5) # 5% of pixels will be black
a = 255/(ceil-floor)
b = floor*255/(floor-ceil)
v = np.maximum(0,np.minimum(255,v*a+b)).astype(np.uint8)
hsv = cv.merge((h,s,v))
rgb = cv.cvtColor(hsv, cv.COLOR_HSV2RGB)
imgPil = Image.fromarray(rgb)
imgPil.save(fileOut)
3

为什么不使用 PythonMagick 呢?它是一个可以让你在Python中使用Image Magick的接口。

7

如果我理解得没错,ImageMagick里的 -level 选项,那么我提供的 level_image 函数应该能满足你的需求。

有两点需要注意:

  • 速度确实可以提高
  • 目前只支持RGB格式的图片
  • 这个算法是通过HSV颜色空间来处理的,只影响V(亮度)这一部分

代码如下:

import colorsys

class Level(object):

    def __init__(self, minv, maxv, gamma):
        self.minv= minv/255.0
        self.maxv= maxv/255.0
        self._interval= self.maxv - self.minv
        self._invgamma= 1.0/gamma

    def new_level(self, value):
        if value <= self.minv: return 0.0
        if value >= self.maxv: return 1.0
        return ((value - self.minv)/self._interval)**self._invgamma

    def convert_and_level(self, band_values):
        h, s, v= colorsys.rgb_to_hsv(*(i/255.0 for i in band_values))
        new_v= self.new_level(v)
        return tuple(int(255*i)
                for i
                in colorsys.hsv_to_rgb(h, s, new_v))

def level_image(image, minv=0, maxv=255, gamma=1.0):
    """Level the brightness of image (a PIL.Image instance)
    All values ≤ minv will become 0
    All values ≥ maxv will become 255
    gamma controls the curve for all values between minv and maxv"""

    if image.mode != "RGB":
        raise ValueError("this works with RGB images only")

    new_image= image.copy()

    leveller= Level(minv, maxv, gamma)
    levelled_data= [
        leveller.convert_and_level(data)
        for data in image.getdata()]
    new_image.putdata(levelled_data)
    return new_image

如果有办法用PIL库进行RGB和HSV之间的转换,那么可以把图片分成H、S、V三个部分,使用V部分的 .point 方法,然后再转换回RGB,这样可以大大加快处理速度;不过,我还没找到这样的办法。

撰写回答