Performing the same action as ImageMagick "-level" in python / PIL?

I want to adjust image color levels in python. I can use any python library that can be easily installed on the Ubuntu desktop. I want to do the same thing as ImageMagick -level( http://www.imagemagick.org/www/command-line-options.html#level ). PIL (Python Image Library) does not seem to have this. I called up convertin the image and then read again in the file, but that seems wasteful. Is there a better / faster way?

+3
source share
4 answers

If I understand the parameter -levelfor ImageMagick correctly , then the function level_imagethat I provide should do what you want.

Two things to note:

  • speed can be improved.
  • it only works with RGB images
  • the algorithm passes through the HSV color space and affects only the V component (brightness)

The code:

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

If there is a way to do RGB → HSV conversion (and vice versa) using PIL, then you can split it into H, S, V bands, use the .pointV-band method and convert it back to RGB, speeding up the process a lot; however, I did not find such a method.

+6
source

Why not use PythonMagick ? This is the Python interface for Image Magick.

+3
source

, . , 1) HSV , 2) .

The code can be changed to avoid the use of cushion, since openCV uses empty arrays as internal data. If you do this, keep in mind that openCV's native color space is BGR. You will have to change the calls to cv.cvtColor () accordingly.

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)
+1
source

using the code at this link here

# Auto leveling for image

    def levels(data, all_same = 0, clip = 0): 

        if data.mode not in ['RGB', 'CMYK']: 
            return data 

        ## get redistriputed histogram scalled smoothly
        lut = _makelut(data, all_same, clip) 

        ## update image points using histogram
        data = data.point(lut) 

        return data 

    def _find_hi_lo(lut, clip): 
        min = None 
        max = None 

        for i in range(len(lut)): 
            if lut[i] > clip: 
                min = i 
                break 

        lut.reverse() 

        for i in range(len(lut)): 
            if lut[i] > clip: 
                max = 255 - i 
                break 

        return min, max 

    def _scale(channels, min, max): 
        lut = []
        # hefny fix
        ratio = float(max-min)
        if ratio == 0:
            ratio = 1

        for i in range (channels): 
            for i in range(256): 
                value = int((i - min)*(255.0/ratio)) 
                if value < 0: 
                    value = 0 
                if value > 255: 
                    value = 255 
                lut.append(value) 

        return lut 


    def _makelut(data, all_same, clip): 

        histogram = data.histogram() 

        lut = [] 
        r, g, b, k = [], [], [], [] 

        channels = len(histogram)/256 

        for i in range(256): 
            r.append(histogram[i]) 
            g.append(histogram[256+i]) 
            b.append(histogram[512+i]) 
        if channels == 4: 
            for i in range(256): 
                k.append(histogram[768+i]) 


        rmin, rmax = _find_hi_lo(r, clip) 
        gmin, gmax = _find_hi_lo(g, clip) 
        bmin, bmax = _find_hi_lo(b, clip) 
        if channels == 4: 
            kmin, kmax = _find_hi_lo(k) 
        else: 
            kmin, kmax = 128, 128 

        if all_same == 1: 

            min_max = [rmin, gmin, bmin, kmin, rmax, gmax, bmax, kmax] 
            min_max.sort() 
            lut = _scale(channels, min_max[0], min_max[-1]) 

        else: 

            r_lut = _scale(1, rmin, rmax) 
            g_lut = _scale(1, gmin, gmax) 
            b_lut = _scale(1, bmin, bmax) 
            if channels == 4: 
                k_lut = _scale(1, kmin, kmax) 

            lut = [] 

            for i in range (256): 
                lut.append(r_lut[i]) 
            for i in range (256): 
                lut.append(g_lut[i]) 
            for i in range (256): 
                lut.append(b_lut[i]) 
            if channels == 4: 
                for i in range (256): 
                    lut.append(k_lut[i]) 

        return lut 

from PIL import ImageEnhance , ImageDraw , Image
img = Image.open(file_path)
img2 = levels(img)
0
source

Source: https://habr.com/ru/post/1751470/


All Articles