Библиотека PIL Image.fromarray () вызывает AttributeError: у объекта 'tuple' нет атрибута '__array_interface__' - PullRequest
0 голосов
/ 10 мая 2018

Я использую симулятор физики pybullet и хочу сохранить изображение из «виртуальной камеры» в моей симуляции, используя следующие две строки кода.

camera1 = pybullet.getCameraImage(900,600)
im1 = Image.fromarray(camera1[2])

В первой строке используется функция getCameraImage pybullet для возврата необработанного изображения. camera1[2] - это список цветов пикселей в формате RGBA в диапазоне [0..255] для каждого цвета.
Вторая строка должна взять этот массив и преобразовать его в изображение, которое затем я могу сохранить и просмотреть.

Когда я запускаю код, я получаю следующее сообщение об ошибке:

Traceback (most recent call last):
  File "generate_dataset.py", line 43, in <module>
    im1 = Image.fromarray(camera1[2], "RGBA")
  File "/usr/lib/python3/dist-packages/PIL/Image.py", line 2140, in 
fromarray
    arr = obj.__array_interface__
AttributeError: 'tuple' object has no attribute '__array_interface__'

Код работал вчера на Ubuntu 14.04, но сегодня я обновился до Ubuntu 16.04, и код перестал работать. Я попытался запустить его с Python 2.7.12 и Python 3.5.2, обе версии получают ту же ошибку.

Вещи, которые я пробовал:
Добавление еще одной строки для преобразования списка в массив numpy:
camera1 = p.getCameraImage(900,600) imarray = np.asarray(camera1[2]) im1 = Image.fromarray(imarray)
Результат:

Traceback (most recent call last):
  File "generate_dataset.py", line 42, in <module>
    im1 = Image.fromarray(imarray)
  File "/usr/local/lib/python2.7/dist-packages/PIL/Image.py", line 2431, in fromarray
    raise TypeError("Cannot handle this data type")
TypeError: Cannot handle this data type

Изменение последней строки на:
im1 = Image.fromarray(imarray.astype('uint8'))
Результат:

Traceback (most recent call last):
  File "generate_dataset.py", line 42, in <module>
    im1 = Image.fromarray(imarray.astype('uint8'))
  File "/usr/lib/python3/dist-packages/PIL/Image.py", line 2165, in fromarray
    size = shape[1], shape[0]
IndexError: tuple index out of range

Дополнительная информация, если необходимо

Документация Pybullet: https://docs.google.com/document/d/10sXEhzFRSnvFcl3XxNGhnD4N2SedqwdAvK3dsihxVUA/edit#heading=h.2ye70wns7io3

Документация PIL: https://pillow.readthedocs.io/en/3.1.x/reference/Image.html

Мой полный код:

import pybullet as p
import pybullet_data
import time
from PIL import Image
from random import *
import numpy as np

nExamples = 200

for n in range(0, nExamples):

  print ("Running example " + str(n))

  physicsClient = p.connect(p.DIRECT) #or p.GUI for graphical version
  p.setAdditionalSearchPath(pybullet_data.getDataPath()) #optionally
  p.setGravity(0,0,-10)
  planeId = p.loadURDF("ground.urdf")
  p.resetDebugVisualizerCamera( cameraDistance=1, cameraYaw=0, cameraPitch=-30, cameraTargetPosition=[0,0,0])

  x1 = uniform(-0.03,0.03)
  y1 = uniform(-0.03,0.03)
  x2 = x1 + uniform(-0.03,0.03)
  y2 = y1 + uniform(-0.03,0.03)
  cubeStartPos = [0.0,0,0.025]
  cubeStartPos1 = [x1,y1,0.075]
  cubeStartPos2 = [x2,y2,0.125]
  yaw0 = uniform(0,np.pi/2)
  yaw1 = uniform(0,np.pi/2)
  yaw2 = uniform(0,np.pi/2)
  cubeStartOrientation0 = p.getQuaternionFromEuler([0,0,yaw0])
  cubeStartOrientation1 = p.getQuaternionFromEuler([0,0,yaw1])
  cubeStartOrientation2 = p.getQuaternionFromEuler([0,0,yaw2])
  boxId = p.loadURDF("red_block.urdf",cubeStartPos, cubeStartOrientation0)
  boxId1 = p.loadURDF("green_block.urdf",cubeStartPos1, cubeStartOrientation1)
  boxId2 = p.loadURDF("blue_block.urdf",cubeStartPos2, cubeStartOrientation2)

  #saving the initial image...
  camera1 = p.getCameraImage(900,600)
  imarray = np.asarray(camera1[2])
  im1 = Image.fromarray(imarray.astype('uint8'))

  for i in range (250):
    p.stepSimulation()
    time.sleep(1./20.)

  camera2 = p.getCameraImage(900,600)

  #saving the image after blocks movement --> if stable this image is equal to the initial...
  im2 = Image.fromarray(camera2[2])

  #Are the images different? (Is it unstable?) --> if yes then diff is large, otherwise, diff is negligible
  diff = (camera2[2] - camera1[2]).sum()
  print("DIFFERENCE =", diff)
  if abs(diff) < 100000:
    im1.save("images/stable/image_%d.png" % n)
  else:
    im1.save("images/unstable/image_%d.png" % n)

  #cropping images
  cropped = im1.crop((350,200,550,400))
  cropped.save("images/cropped/image_%d.png" % n)

  p.disconnect()

  print ("Reached end of loop\n")

1 Ответ

0 голосов
/ 10 мая 2018

Из вашего описания не ясно, является ли camera1[2] простым списком последовательных значений R, G, B, A или списком RGBA-кортежей.Итак, я покажу вам, как читать оба варианта.;)

Ваша главная проблема в том, что ваши данные не содержат информации о ширине и высоте, поэтому нам нужно как-то предоставить эту информацию.Один из способов сделать это - прочитать данные в массив Numpy 3D правильной формы.Но мы также можем сделать это напрямую в PIL, используя соответствующие методы Image.

Для моих демонстраций я использую циклы Python для создания простых данных RGBA.

Этот скрипт создает список кортежей RGBA.

from PIL import Image

maxval = 255
width, height = 400, 300

# Display size info
size = width * height
fmt = 'Width: {}, Height: {}, Pixels: {}, Bytes: {}'
print(fmt.format(width, height, size, size * 4))

# Make a 2D gradient that starts at black in the top left corner,
# with red & green increasing horizontally, blue increasing vertically.
# This would be much faster using Numpy instead of Python loops.
pixels = []
# Make all pixels fully opaque
alpha = maxval
for y in range(height):
    blu = maxval * y // height
    for x in range(width):
        red = gre = maxval * x // width
        # Make a single RGBA pixel as a tuple
        pix = red, gre, blu, alpha
        # And save it
        pixels.append(pix)

# Show that the size of `pixels` is correct and show the first few pixels
print('Size:', len(pixels))
print(pixels[:8])

# Make a new image object. All pixels are set to black. 
img = Image.new('RGBA', (width, height))
# Copy the pixel data to the Image
img.putdata(pixels)
img.show()
img.save('test1.png') 

выход

Width: 400, Height: 300, Pixels: 120000, Bytes: 480000
Size: 120000
[(0, 0, 0, 255), (0, 0, 0, 255), (1, 1, 0, 255), (1, 1, 0, 255), (2, 2, 0, 255), (3, 3, 0, 255), (3, 3, 0, 255), (4, 4, 0, 255)]

test1.png

2D fade from black to yellow, blue, white


Этот скрипт создает плоский список значений R, G, B, A.Он использует объект Python 3 bytes, поэтому он не будет работать должным образом на Python 2.

from PIL import Image

maxval = 255
width, height = 400, 300

# Display size info
size = width * height
fmt = 'Width: {}, Height: {}, Pixels: {}, Bytes: {}'
print(fmt.format(width, height, size, size * 4))

# Make a 2D gradient that starts at black in the top left corner,
# with red & green increasing horizontally, blue increasing vertically.
# This would be much faster using Numpy instead of Python loops.
rgba = []
# Make all pixels fully opaque
alpha = maxval
for y in range(height):
    blu = maxval * y // height
    for x in range(width):
        red = gre = maxval * x // width
        # Make a single RGBA pixel as a tuple
        pix = red, gre, blu, alpha
        # And save each of red, gre, blu, alpha to rgba. 
        # By using `.extend` we create a flat list
        rgba.extend(pix)

# Show that the size of `rgba` is correct and show the first few values.
print('Size:', len(rgba))
print(rgba[:32])

# Convert the rgba list to bytes.
rgba = bytes(rgba)
# Make a new image object from the bytes
img = Image.frombytes('RGBA', (width, height), rgba)
img.show()
img.save('test2.png')

output

Width: 400, Height: 300, Pixels: 120000, Bytes: 480000
Size: 480000
[0, 0, 0, 255, 0, 0, 0, 255, 1, 1, 0, 255, 1, 1, 0, 255, 2, 2, 0, 255, 3, 3, 0, 255, 3, 3, 0, 255, 4, 4, 0, 255]

file 'test2.png 'идентичен' test1.png '.

...