วิธีการแปลงภาพ PIL เป็นอาร์เรย์ numpy?


257

เอาล่ะฉันกำลังเล่นกับการแปลงวัตถุรูปภาพ PIL ไปมาเป็นอาร์เรย์ที่มีจำนวนมากดังนั้นฉันจึงสามารถแปลงพิกเซลได้เร็วขึ้นโดยการแปลงพิกเซลกว่าPixelAccessวัตถุของ PIL ฉันพบวิธีวางข้อมูลพิกเซลในอาร์เรย์แบบสามมิติที่มีประโยชน์โดย:

pic = Image.open("foo.jpg")
pix = numpy.array(pic.getdata()).reshape(pic.size[0], pic.size[1], 3)

แต่ดูเหมือนว่าฉันจะไม่สามารถหาวิธีโหลดมันกลับเข้าไปในวัตถุ PIL ได้หลังจากที่ฉันได้ทำการแปลงที่ยอดเยี่ยมทั้งหมดแล้ว ฉันตระหนักถึงputdata()วิธีการ แต่ดูเหมือนจะไม่สามารถทำให้มันทำงานได้


6
โปรดทราบว่าpic.size[0]และpic.size[1]ควรจะสลับ (ie. reshape(pic.size[1], pic.size[0], 3)) เนื่องจากsizeเป็นwidth x heightหรือในขณะที่แมทริกซ์คือการสั่งซื้อx * y rows x columns
หมอกหนา

คำตอบ:


286

คุณไม่ได้บอกputdata()ว่าไม่ได้ประพฤติตัวอย่างไร ฉันสมมติว่าคุณกำลังทำ

>>> pic.putdata(a)
Traceback (most recent call last):
  File "...blablabla.../PIL/Image.py", line 1185, in putdata
    self.im.putdata(data, scale, offset)
SystemError: new style getargs format but argument is not a tuple

นี่เป็นเพราะputdataคาดว่าจะมีลำดับของสิ่งอันดับและคุณจะได้รับอาร์เรย์จำนวนมาก นี้

>>> data = list(tuple(pixel) for pixel in pix)
>>> pic.putdata(data)

จะทำงาน แต่มันช้ามาก

ตั้งแต่ PIL 1.1.6 วิธีที่"เหมาะสม" ในการแปลงระหว่างรูปภาพและอาร์เรย์ที่มีจำนวนมากนั้นเรียบง่าย

>>> pix = numpy.array(pic)

แม้ว่าอาร์เรย์ผลลัพธ์จะอยู่ในรูปแบบที่แตกต่างจากของคุณ (อาร์เรย์ 3 มิติหรือแถว / คอลัมน์ / rgb ในกรณีนี้)

แล้วหลังจากที่คุณทำการเปลี่ยนแปลงไปยังอาร์เรย์ที่คุณควรจะสามารถที่จะทำอย่างใดอย่างหนึ่งหรือสร้างภาพใหม่ด้วยpic.putdata(pix)Image.fromarray(pix)


2
ก่อนอื่นไม่ควรเป็น pic.putdata (data) และ numpy.asarray (รูป) สร้างอาร์เรย์แบบอ่านอย่างเดียวดังนั้นคุณต้องโทรหา numpy.array (รูป) และคุณไม่ได้ตอบคำถาม ... จากลิงก์ที่คุณระบุว่าเป็นรูป = Image.fromarray ( Pix) แก้ไขคำตอบของคุณและฉันจะยอมรับมัน
akdom

2
ขอบคุณสำหรับสิ่งนี้ ... Image.fromarrayไม่ได้ระบุไว้ในเอกสาร PIL (!) ดังนั้นฉันไม่เคยพบถ้ามันไม่ได้สำหรับสิ่งนี้
นาธานรีด

13
ที่แสดงรายการหน้าnumpy.asarray(pic)เป็นวิธีที่ "เหมาะสม" numpy.array(pic)การแปลงไม่ ตามคำตอบนี้ arrayจะทำสำเนาในขณะที่asarrayจะไม่ (แต่asarrayผลลัพธ์จะเป็นแบบอ่านอย่างเดียว)
Arthur Tacca

1
คำเตือนที่นี่ (จากความผิดพลาดของฉันเอง): คุณต้องพิจารณาขนาดและช่วงของข้อมูลด้วย ในหลาย ๆ usecases คุณจะต้องแสดงรูปภาพที่มีขนาด 0-255 ไบต์ แต่คุณอาจคาดหวังว่าสิ่งเหล่านี้จะถูกแปลงเป็น 0.0-1.0 ในอาร์เรย์ที่มีค่า บางหน่วยการแปลงจาก uint8 ทำเช่นนี้ แต่ในกรณีนี้มันไม่ได้ .. ดังนั้นลองดู :)
34417

คำตอบที่สองดีกว่า
นาธาน

193

เปิดIเป็นอาร์เรย์:

>>> I = numpy.asarray(PIL.Image.open('test.jpg'))

ทำบางสิ่งเพื่อIแล้วแปลงเป็นรูปภาพ:

>>> im = PIL.Image.fromarray(numpy.uint8(I))

กรองภาพที่ไม่มีค่าด้วย FFT, Python

หากคุณต้องการที่จะทำอย่างชัดเจนด้วยเหตุผลบางอย่างมีฟังก์ชั่น pil2array () และ array2pil () โดยใช้ getdata () ในหน้านี้ใน correlation.zip


2
@ArditS .: คุณมาimport Imageก่อนหรือไม่ คุณติดตั้ง PIL แล้วหรือยัง
endolith

5
การuint8แปลงจำเป็นหรือไม่?
Neil Traft

4
numpy.asarray(Image.open(filename))ดูเหมือนว่าจะทำงานกับ. jpg ภาพ แต่ไม่ใช่สำหรับ. png array(<PngImagePlugin.PngImageFile image mode=LA size=500x500 at 0x3468198>, dtype=object)ผลที่ได้แสดงเป็น ดูเหมือนจะไม่มีวิธีการที่ระบุชื่ออย่างชัดเจนของPngImagePlugin.PngImageFileวัตถุสำหรับการแก้ปัญหานี้ เดาฉันควรถามคำถามนี้เป็นคำถามใหม่ แต่มีความเกี่ยวข้องกับหัวข้อนี้มาก มีใครเข้าใจไหมว่าเกิดอะไรขึ้นที่นี่
jez

3
@Rebs: นี่คือเหตุผลว่าทำไมจึงเร็วกว่านี้มาก: getdata()ส่งคืนลำดับเช่น object ( pillow.readthedocs.io/en/3.4.x/reference/ ...... ) แต่ภาพหมอนใช้__array_interface__ซึ่งnumpyสามารถใช้เพื่อเข้าถึงไบต์ดิบ ของรูปภาพโดยไม่ต้องผ่านตัววนซ้ำ (ดูgithub.com/python-pillow/Pillow/blob/ ......และdocs.scipy.org/doc/numpy/reference/arrays.interface.html ) คุณสามารถใช้งานได้numpy.array(PIL.Image.open('test.jpg'))
tdp2110

3
@jez ตรวจสอบว่าวัตถุรูปภาพถูกปิดก่อนที่คุณจะแปลงเป็นจำนวนเต็มหรือไม่ เกิดขึ้นกับฉันและฉันพบว่าฉันปิดวัตถุภาพที่ไหนสักแห่ง
Shaohua Li

65

ฉันใช้ Pillow 4.1.1 (ตัวตายตัวแทนของ PIL) ใน Python 3.5 การแปลงระหว่างหมอนและหมอนตรงไปตรงมา

from PIL import Image
import numpy as np
im = Image.open('1.jpg')
im2arr = np.array(im) # im2arr.shape: height x width x channel
arr2im = Image.fromarray(im2arr)

สิ่งหนึ่งที่ต้องสังเกตคือหมอนสไตล์imเป็นเสาหลักในขณะที่ลักษณะของหมอนim2arrเป็นแถวหลัก อย่างไรก็ตามฟังก์ชั่นImage.fromarrayจะคำนึงถึงเรื่องนี้อยู่แล้ว นั่นคือarr2im.size == im.sizeและarr2im.mode == im.modeในตัวอย่างข้างต้น

เราควรดูแลรูปแบบข้อมูล HxWxC เมื่อประมวลผลอาร์เรย์ numpy ที่ถูกแปลงเช่นทำการแปลงim2arr = np.rollaxis(im2arr, 2, 0)หรือim2arr = np.transpose(im2arr, (2, 0, 1))เป็นรูปแบบ CxHxW


2
นี่เป็นเรื่องเกี่ยวกับตัวอย่างที่ชัดเจนที่สุดรวมถึงข้อความสั่งการนำเข้า (ขอบคุณสำหรับรายละเอียด) ลองโหวตคำตอบนี้เพื่อเพิ่มการมองเห็น
David Parks

ฉันพบว่าเมื่อฉันแปลงรูปภาพที่ดึง PIL ไปเป็นอาเรย์แบบ numpy เมื่อใช้ matplotlib imshow บนอาเรย์มันแสดงให้เห็นว่ามันกลับหัวกลับหางต้องมีnp.flipudการแก้ไข แม้ว่าภาพ PIL ImageDraw.Drawของฉันถูกสร้างขึ้นตั้งแต่เริ่มต้นใช้ ฉันคิดว่าต้องระวังจุดเริ่มต้นของพิกัดของพวกเขา
CMCDragonkai

อวยพรคุณ !! ฉันค้นหาคำตอบนี้มาครึ่งวันแล้ว มันแก้ปัญหาของฉันในการกู้คืนแกนดั้งเดิมหลังจากภาพพล็อตกลับไปยังแกนดั้งเดิม
Tinkerbell

16

คุณจำเป็นต้องแปลงภาพของคุณเป็นอาร์เรย์ numpy ด้วยวิธีนี้:

import numpy
import PIL

img = PIL.Image.open("foo.jpg").convert("L")
imgarr = numpy.array(img) 

วิธีการแปลงนี้จะรักษาภาพ แต่ทำให้สีหายไป อย่างไรก็ตามเพื่อหลีกเลี่ยงการสูญเสียสี?
Moondra

7
@moondra หากฉันเข้าใจคำถามของคุณคุณสามารถแทนที่.convert("L") โดย.convert("RGB")
Billal Begueradj

3

ตัวอย่างที่ฉันใช้ในวันนี้:

import PIL
import numpy
from PIL import Image

def resize_image(numpy_array_image, new_height):
    # convert nympy array image to PIL.Image
    image = Image.fromarray(numpy.uint8(numpy_array_image))
    old_width = float(image.size[0])
    old_height = float(image.size[1])
    ratio = float( new_height / old_height)
    new_width = int(old_width * ratio)
    image = image.resize((new_width, new_height), PIL.Image.ANTIALIAS)
    # convert PIL.Image into nympy array back again
    return array(image)

0

หากรูปภาพของคุณถูกจัดเก็บในรูปแบบ Blob (เช่นในฐานข้อมูล) คุณสามารถใช้เทคนิคเดียวกับที่ Billal Begueradj อธิบายเพื่อแปลงภาพจาก Blobs เป็นอาร์เรย์ไบต์

ในกรณีของฉันฉันต้องการรูปภาพที่เก็บไว้ในคอลัมน์หยดในตาราง db:

def select_all_X_values(conn):
    cur = conn.cursor()
    cur.execute("SELECT ImageData from PiecesTable")    
    rows = cur.fetchall()    
    return rows

ฉันสร้างฟังก์ชันผู้ช่วยเพื่อเปลี่ยนชุดข้อมูลเป็น np.array:

X_dataset = select_all_X_values(conn)
imagesList = convertToByteIO(np.array(X_dataset))

def convertToByteIO(imagesArray):
    """
    # Converts an array of images into an array of Bytes
    """
    imagesList = []

    for i in range(len(imagesArray)):  
        img = Image.open(BytesIO(imagesArray[i])).convert("RGB")
        imagesList.insert(i, np.array(img))

    return imagesList

หลังจากนี้ฉันสามารถใช้ byteArrays ใน Neural Network ของฉันได้

plt.imshow(imagesList[0])

0

แปลงNumpy to PILภาพและPIL to Numpy

import numpy as np
from PIL import Image

def pilToNumpy(img):
    return np.array(img)

def NumpyToPil(img):
    return Image.fromarray(img)

-1
def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

คุณสามารถแปลงรูปภาพให้เป็นจำนวน numpy โดยแยกวิเคราะห์รูปภาพเป็นฟังก์ชั่น numpy () หลังจาก squishing ออกคุณสมบัติ (การทำให้ผิดปกติ)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.