1. 程式人生 > >[Python] 影象簡單處理(PIL or Pillow)

[Python] 影象簡單處理(PIL or Pillow)

前幾天弄了下django的圖片上傳,上傳之後還需要做些簡單的處理,python中PIL模組就是專門用來做這個事情的。

於是照葫蘆畫瓢做了幾個常用圖片操作,在這裡記錄下,以便備用。 

這裡有個字型檔案,大家可以在自己的系統中選取一個,我這打包放在網盤中  下載

一 圖樣

原始圖片


操作一: 縮圖(通常不用這個方式,因為圖片質量損壞太大)


操作二 : 旋轉圖片中的某一部分


操作三: 給圖片新增一個圖片水印, 2張圖層合併

     

操作四: 給圖片新增文字水印,這個用的比較多, 我這裡弄了個白色通明低,可以弄成完全透明的


操作 五 等比壓縮(比較適合做縮圖)


操作六 按照比例剪裁之後,等比壓縮,有時候需要定比例的圖片可以這麼做


二 程式碼

# -*- encoding=utf-8 -*-
'''
author: orangleliu
pil處理圖片,驗證,處理
大小,格式 過濾
壓縮,截圖,轉換

圖片庫最好用Pillow
還有一個測試圖片test.jpg, 一個log圖片,一個字型檔案
'''

#圖片的基本引數獲取
try:
    from PIL import Image, ImageDraw, ImageFont, ImageEnhance
except ImportError:
    import Image, ImageDraw, ImageFont, ImageEnhance

def compress_image(img, w=128, h=128):
    '''
    縮圖
    '''
    img.thumbnail((w,h))
    im.save('test1.png', 'PNG')
    print u'成功儲存為png格式, 壓縮為128*128格式圖片'

def cut_image(img):
    '''
    截圖, 旋轉,再貼上
    '''
    #eft, upper, right, lower
    #x y z w  x,y 是起點, z,w是偏移值
    width, height = img.size
    box = (width-200, height-100, width, height)
    region = img.crop(box)
    #旋轉角度
    region = region.transpose(Image.ROTATE_180)
    img.paste(region, box)
    img.save('test2.jpg', 'JPEG')
    print u'重新拼圖成功'

def logo_watermark(img, logo_path):
    '''
    新增一個圖片水印,原理就是合併圖層,用png比較好
    '''
    baseim = img
    logoim = Image.open(logo_path)
    bw, bh = baseim.size
    lw, lh = logoim.size
    baseim.paste(logoim, (bw-lw, bh-lh))
    baseim.save('test3.jpg', 'JPEG')
    print u'logo水印組合成功'

def text_watermark(img, text, out_file="test4.jpg", angle=23, opacity=0.50):
    '''
    新增一個文字水印,做成透明水印的模樣,應該是png圖層合併
    http://www.pythoncentral.io/watermark-images-python-2x/
    這裡會產生著名的 ImportError("The _imagingft C module is not installed") 錯誤
    Pillow通過安裝來解決 pip install Pillow
    '''
    watermark = Image.new('RGBA', img.size, (255,255,255)) #我這裡有一層白色的膜,去掉(255,255,255) 這個引數就好了

    FONT = "msyh.ttf"
    size = 2

    n_font = ImageFont.truetype(FONT, size)                                       #得到字型
    n_width, n_height = n_font.getsize(text)
    text_box = min(watermark.size[0], watermark.size[1])
    while (n_width+n_height <  text_box):
        size += 2
        n_font = ImageFont.truetype(FONT, size=size)
        n_width, n_height = n_font.getsize(text)                                   #文字逐漸放大,但是要小於圖片的寬高最小值

    text_width = (watermark.size[0] - n_width) / 2
    text_height = (watermark.size[1] - n_height) / 2
    #watermark = watermark.resize((text_width,text_height), Image.ANTIALIAS)
    draw = ImageDraw.Draw(watermark, 'RGBA')                                       #在水印層加畫筆
    draw.text((text_width,text_height),
              text, font=n_font, fill="#21ACDA")
    watermark = watermark.rotate(angle, Image.BICUBIC)
    alpha = watermark.split()[3]
    alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
    watermark.putalpha(alpha)
    Image.composite(watermark, img, watermark).save(out_file, 'JPEG')
    print u"文字水印成功"


#等比例壓縮圖片
def resizeImg(img, dst_w=0, dst_h=0, qua=85):
    '''
    只給了寬或者高,或者兩個都給了,然後取比例合適的
    如果圖片比給要壓縮的尺寸都要小,就不壓縮了
    '''
    ori_w, ori_h = im.size
    widthRatio = heightRatio = None
    ratio = 1

    if (ori_w and ori_w > dst_w) or (ori_h and ori_h  > dst_h):
        if dst_w and ori_w > dst_w:
            widthRatio = float(dst_w) / ori_w                                      #正確獲取小數的方式
        if dst_h and ori_h > dst_h:
            heightRatio = float(dst_h) / ori_h

        if widthRatio and heightRatio:
            if widthRatio < heightRatio:
                ratio = widthRatio
            else:
                ratio = heightRatio

        if widthRatio and not heightRatio:
            ratio = widthRatio

        if heightRatio and not widthRatio:
            ratio = heightRatio

        newWidth = int(ori_w * ratio)
        newHeight = int(ori_h * ratio)
    else:
        newWidth = ori_w
        newHeight = ori_h

    im.resize((newWidth,newHeight),Image.ANTIALIAS).save("test5.jpg", "JPEG", quality=qua)
    print u'等比壓縮完成'

    '''
    Image.ANTIALIAS還有如下值:
    NEAREST: use nearest neighbour
    BILINEAR: linear interpolation in a 2x2 environment
    BICUBIC:cubic spline interpolation in a 4x4 environment
    ANTIALIAS:best down-sizing filter
    '''

#裁剪壓縮圖片
def clipResizeImg(im, dst_w, dst_h, qua=95):
    '''
        先按照一個比例對圖片剪裁,然後在壓縮到指定尺寸
        一個圖片 16:5 ,壓縮為 2:1 並且寬為200,就要先把圖片裁剪成 10:5,然後在等比壓縮
    '''
    ori_w,ori_h = im.size

    dst_scale = float(dst_w) / dst_h  #目標高寬比
    ori_scale = float(ori_w) / ori_h #原高寬比

    if ori_scale <= dst_scale:
        #過高
        width = ori_w
        height = int(width/dst_scale)

        x = 0
        y = (ori_h - height) / 2

    else:
        #過寬
        height = ori_h
        width = int(height*dst_scale)

        x = (ori_w - width) / 2
        y = 0

    #裁剪
    box = (x,y,width+x,height+y)
    #這裡的引數可以這麼認為:從某圖的(x,y)座標開始截,截到(width+x,height+y)座標
    #所包圍的影象,crop方法與php中的imagecopy方法大為不一樣
    newIm = im.crop(box)
    im = None

    #壓縮
    ratio = float(dst_w) / width
    newWidth = int(width * ratio)
    newHeight = int(height * ratio)
    newIm.resize((newWidth,newHeight),Image.ANTIALIAS).save("test6.jpg", "JPEG",quality=95)
    print  "old size  %s  %s"%(ori_w, ori_h)
    print  "new size %s %s"%(newWidth, newHeight)
    print u"剪裁後等比壓縮完成"


if __name__ == "__main__":
    '''
    主要是實現功能, 程式碼沒怎麼整理
    '''
    im = Image.open('test.jpg')  #image 物件
    compress_image(im)

    im = Image.open('test.jpg')  #image 物件
    cut_image(im)

    im = Image.open('test.jpg')  #image 物件
    logo_watermark(im, 'logo.png')

    im = Image.open('test.jpg')  #image 物件
    text_watermark(im, 'Orangleliu')

    im = Image.open('test.jpg')  #image 物件
    resizeImg(im, dst_w=100, qua=85)

    im = Image.open('test.jpg')  #image 物件
    clipResizeImg(im, 100, 200)


三 參考