ฉันจะเพิ่มมิติข้อมูลใหม่ให้กับอาร์เรย์ Numpy ได้อย่างไร


94

ฉันเริ่มต้นด้วยอาร์เรย์จำนวนนับของรูปภาพ

In[1]:img = cv2.imread('test.jpg')

รูปร่างคือสิ่งที่คุณคาดหวังสำหรับภาพ RGB ขนาด 640x480

In[2]:img.shape
Out[2]: (480, 640, 3)

อย่างไรก็ตามภาพที่ฉันมีคือเฟรมวิดีโอซึ่งมีความยาว 100 เฟรม จะเป็นการดีที่ฉันต้องการจะมีอาร์เรย์เดียวที่มีข้อมูลทั้งหมดจากวิดีโอนี้ดังกล่าวว่าผลตอบแทนimg.shape(480, 640, 3, 100)

วิธีใดที่ดีที่สุดในการเพิ่มเฟรมถัดไปนั่นคือข้อมูลรูปภาพชุดถัดไปอาร์เรย์ 480 x 640 x 3 อีกชุดในอาร์เรย์เริ่มต้นของฉัน

คำตอบ:


105

คุณกำลังถามว่าจะเพิ่มมิติข้อมูลให้กับอาร์เรย์ NumPy อย่างไรเพื่อให้มิตินั้นเติบโตขึ้นเพื่อรองรับข้อมูลใหม่ สามารถเพิ่มมิติข้อมูลได้ดังนี้:

image = image[..., np.newaxis]

10
ปัจจุบันnumpy.newaxisถูกกำหนดให้เป็นNone(ในไฟล์numeric.py) ดังนั้นคุณสามารถใช้ `image = image [... , None] ได้
Ray

60
อย่าใช้None. ใช้np.newaxisเพราะอย่างชัดเจนดีกว่าโดยนัย
Neil G

7
จะเป็นไปได้อย่างไร? Noneไม่ได้หมายความถึงอะไร เป็นเรื่องที่ชัดเจน มันคือNone. ระบุไว้ชัดเจน. None เป็นสิ่งในหลาม ไม่มีข้อสงสัยใด ๆ Noneเป็นรายละเอียดสุดท้ายคุณไม่สามารถลงลึกได้ บนมืออื่น ๆ ที่แสดงถึงnumpy.newaxis Noneโดยพื้นฐานNoneแล้ว มันคือNone. แต่Noneโดยปริยาย. มันเป็นแต่ไม่แสดงออกโดยตรงเป็นNone อย่างชัดเจนระบุไว้อย่างชัดเจนและในรายละเอียดออกจากห้องเพื่อความสับสนหรือข้อสงสัยใด ๆ แนะนำโดยปริยายแม้ว่าจะไม่ได้แสดงออกโดยตรง ฉันต้องเพิ่มว่าจากมุมมองของ API จะปลอดภัยในการใช้งาน None numpy.newaxis
Pedro Rodrigues

3
เดาในที่นี้การระบุอย่างชัดเจนหมายถึง "เจตนาของผู้เขียนโค้ด" แทนที่จะเป็นความชัดเจนเชิงวาทกรรม / ความหมาย
Gabrer

ควรเลือกคำตอบของ JoshAdel เป็นคำตอบที่ถูกต้องในกรณีนี้และต้องการคะแนนเสียงเพิ่มเติม ประเด็นของเขามีความสำคัญในการที่ OP ต้องการเพิ่ม nparray ที่มีมิติสูงขึ้นในขณะที่เขาไป ไม่สามารถเพิ่มขนาดของ ndarray ได้เมื่อสร้างแล้วต้องทำสำเนา คำตอบนี้จะทำให้เป็นรูปร่างเท่านั้น (480, 640, 3, 1) และทุกครั้งที่คุณเพิ่มเฟรมใหม่คุณจะต้องทำสำเนาอีกครั้ง ไม่ดี.
Dan Boschen

61

อีกทางเลือกหนึ่งคือ

image = image[..., np.newaxis]

ในคำตอบของ @dblissคุณสามารถใช้numpy.expand_dimslike

image = np.expand_dims(image, <your desired dimension>)

ตัวอย่างเช่น (นำมาจากลิงค์ด้านบน):

x = np.array([1, 2])

print(x.shape)  # prints (2,)

แล้ว

y = np.expand_dims(x, axis=0)

ผลตอบแทน

array([[1, 2]])

และ

y.shape

ให้

(1, 2)

จะเพิ่มค่าในมิติใหม่ได้อย่างไร ถ้าฉันทำy[1,0]ดัชนีผิดพลาด y[0,1]สามารถเข้าถึงได้
weima

@weima: ไม่แน่ใจว่าคุณเป็นอย่างไร ผลลัพธ์ที่คุณต้องการคืออะไร?
Cleb

26

คุณสามารถสร้างอาร์เรย์ของขนาดที่ถูกต้องขึ้นด้านหน้าและเติม:

frames = np.empty((480, 640, 3, 100))

for k in xrange(nframes):
    frames[:,:,:,k] = cv2.imread('frame_{}.jpg'.format(k))

หากเฟรมเป็นไฟล์ jpg แต่ละไฟล์ที่ตั้งชื่อในลักษณะเฉพาะ (ในตัวอย่าง frame_0.jpg, frame_1.jpg ฯลฯ )

โปรดทราบว่าคุณอาจพิจารณาใช้(nframes, 480,640,3)อาร์เรย์ที่มีรูปร่างแทน


1
ฉันคิดว่านี่คือวิธีที่จะไป หากคุณใช้การเรียงต่อกันคุณจะต้องย้ายอาร์เรย์ในหน่วยความจำทุกครั้งที่คุณเพิ่มเข้าไป สำหรับ 100 เฟรมที่ไม่สำคัญเลย แต่ถ้าคุณต้องการไปที่วิดีโอขนาดใหญ่ BTW ฉันจะใช้จำนวนเฟรมเป็นมิติแรกดังนั้นให้มีอาร์เรย์ (100,480,640,3) เพื่อที่คุณจะสามารถเข้าถึงแต่ละเฟรมได้ (โดยปกติแล้วคุณจะต้องการดูอะไรใช่ไหม) ได้ง่ายขึ้น (F [1 ] แทน F [:,:,:, 1]) แน่นอนประสิทธิภาพที่ชาญฉลาดไม่ควรสำคัญเลย
Magellan88

ฉันเห็นด้วยกับ JoshAdel และ Magellan88 คำตอบอื่น ๆ คือหน่วยความจำที่ไม่มีประสิทธิภาพและเวลาในการประมวลผล - ndarrays ไม่สามารถเพิ่มขนาดได้เมื่อสร้างขึ้นดังนั้นจะมีการทำสำเนาเสมอหากคุณคิดว่าคุณกำลังต่อท้าย
Dan Boschen

12

Pythonic

X = X[:, :, None]

ซึ่งเทียบเท่ากับ

X = X[:, :, numpy.newaxis] และ X = numpy.expand_dims(X, axis=-1)

แต่ในขณะที่คุณกำลังถามอย่างชัดเจนเกี่ยวกับการซ้อนภาพฉันขอแนะนำให้ไปซ้อนlistภาพnp.stack([X1, X2, X3])ที่คุณอาจรวบรวมไว้ในวง

หากคุณไม่ชอบลำดับของมิติข้อมูลที่คุณสามารถจัดเรียงใหม่ได้ np.transpose()


7

คุณสามารถใช้การnp.concatenate()ระบุว่าaxisจะต่อท้ายโดยใช้np.newaxis:

import numpy as np
movie = np.concatenate((img1[:,np.newaxis], img2[:,np.newaxis]), axis=3)

หากคุณกำลังอ่านจากไฟล์จำนวนมาก:

import glob
movie = np.concatenate([cv2.imread(p)[:,np.newaxis] for p in glob.glob('*.jpg')], axis=3)

2

ไม่มีโครงสร้างใน numpy ที่ช่วยให้คุณสามารถผนวกข้อมูลเพิ่มเติมได้ในภายหลัง

แต่ numpy จะทำให้ข้อมูลทั้งหมดของคุณอยู่ในกลุ่มตัวเลขที่ต่อเนื่องกัน (โดยทั่วไปคืออาร์เรย์ C) และการปรับขนาดใด ๆ จำเป็นต้องมีการจัดสรรหน่วยความจำใหม่เพื่อเก็บไว้ ความเร็วของ Numpy มาจากความสามารถในการเก็บข้อมูลทั้งหมดไว้ในอาร์เรย์จำนวนนับในหน่วยความจำเดียวกัน เช่นดำเนินการทางคณิตศาสตร์สามารถparallelized สำหรับความเร็วและคุณได้รับน้อยแคช

ดังนั้นคุณจะมีวิธีแก้ปัญหาสองประเภท:

  1. จัดสรรหน่วยความจำไว้ล่วงหน้าสำหรับอาร์เรย์ numpy และกรอกค่าเช่นในคำตอบของ JoshAdel หรือ
  2. เก็บข้อมูลของคุณไว้ในรายการ python ปกติจนกว่าจะจำเป็นต้องรวบรวมข้อมูลทั้งหมดเข้าด้วยกัน (ดูด้านล่าง)

images = []
for i in range(100):
    new_image = # pull image from somewhere
    images.append(new_image)
images = np.stack(images, axis=3)

โปรดทราบว่าคุณไม่จำเป็นต้องขยายขนาดของอาร์เรย์รูปภาพแต่ละชุดก่อนและคุณไม่จำเป็นต้องทราบจำนวนภาพที่คุณคาดหวังล่วงหน้า


2

พิจารณาแนวทางที่ 1 ด้วยวิธีการปรับรูปร่างและแนวทางที่ 2 ด้วยวิธี np.newaxis ที่ให้ผลลัพธ์เดียวกัน:

#Lets suppose, we have:
x = [1,2,3,4,5,6,7,8,9]
print('I. x',x)

xNpArr = np.array(x)
print('II. xNpArr',xNpArr)
print('III. xNpArr', xNpArr.shape)

xNpArr_3x3 = xNpArr.reshape((3,3))
print('IV. xNpArr_3x3.shape', xNpArr_3x3.shape)
print('V. xNpArr_3x3', xNpArr_3x3)

#Approach 1 with reshape method
xNpArrRs_1x3x3x1 = xNpArr_3x3.reshape((1,3,3,1))
print('VI. xNpArrRs_1x3x3x1.shape', xNpArrRs_1x3x3x1.shape)
print('VII. xNpArrRs_1x3x3x1', xNpArrRs_1x3x3x1)

#Approach 2 with np.newaxis method
xNpArrNa_1x3x3x1 = xNpArr_3x3[np.newaxis, ..., np.newaxis]
print('VIII. xNpArrNa_1x3x3x1.shape', xNpArrNa_1x3x3x1.shape)
print('IX. xNpArrNa_1x3x3x1', xNpArrNa_1x3x3x1)

เรามีผลลัพธ์ดังนี้:

I. x [1, 2, 3, 4, 5, 6, 7, 8, 9]

II. xNpArr [1 2 3 4 5 6 7 8 9]

III. xNpArr (9,)

IV. xNpArr_3x3.shape (3, 3)

V. xNpArr_3x3 [[1 2 3]
 [4 5 6]
 [7 8 9]]

VI. xNpArrRs_1x3x3x1.shape (1, 3, 3, 1)

VII. xNpArrRs_1x3x3x1 [[[[1]
   [2]
   [3]]

  [[4]
   [5]
   [6]]

  [[7]
   [8]
   [9]]]]

VIII. xNpArrNa_1x3x3x1.shape (1, 3, 3, 1)

IX. xNpArrNa_1x3x3x1 [[[[1]
   [2]
   [3]]

  [[4]
   [5]
   [6]]

  [[7]
   [8]
   [9]]]]

1

ฉันทำตามแนวทางนี้:

import numpy as np
import cv2

ls = []

for image in image_paths:
    ls.append(cv2.imread('test.jpg'))

img_np = np.array(ls) # shape (100, 480, 640, 3)
img_np = np.rollaxis(img_np, 0, 4) # shape (480, 640, 3, 100).
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.