แบ่งเฟรมอาร์เรย์ numpy หลายเฟรมด้วยหลาย y1: y2, x1: x2
ฉันมีอาร์เรย์จำนวนมากของหลายเฟรม (หลายเฟรม) และฉันต้องการแบ่งความสูงและความกว้างของแต่ละเฟรมด้วย y1, y2, x1, x2 ที่แตกต่างกันเพื่อวาดสี่เหลี่ยม "1" ในแต่ละเฟรม (slice_yyxx) เป็นอาร์เรย์ที่เป็นตัวเลขและมีหนึ่งอาร์เรย์ของ y1, y2, x1, x2 สำหรับแต่ละเฟรม
slice_yyxx = np.array(slice_yyxx).astype(int)
nbr_frame = slice_yyxx.shape[0]
multiple_frames = np.zeros(shape=(nbr_frame, target_shape[0], target_shape[1], target_shape[2]))
print(multiple_frames.shape)
# (5, 384, 640, 1)
print(slice_yyxx)
# Value ok
print(slice_yyxx.shape)
# (5, 4)
# Then 5 array of coord like [y1, y2, x1, x2] for slice each frames
print(slice_yyxx.dtype)
# np.int64
multiple_frames[:, slice_yyxx[:,0]:slice_yyxx[:,1], slice_yyxx[:,2]:slice_yyxx[:,3]] = 1
# ERROR: TypeError: only integer scalar arrays can be converted to a scalar index
คำตอบ
คำถามที่แท้จริงต่อไปนี้คือวิธีแปลงชิ้นส่วนตามอำเภอใจให้เป็นสิ่งที่คุณสามารถใช้ได้ในหลายมิติโดยไม่ต้องวนซ้ำ ฉันจะวางตัวว่าเคล็ดลับคือการใช้การรวมกันฉลาดของการจัดทำดัชนีแฟนซีและarangerepeat
เป้าหมายคือการสร้างอาร์เรย์ของดัชนีแถวและคอลัมน์ที่สอดคล้องกับแต่ละมิติ ลองพิจารณากรณีง่ายๆที่เห็นภาพได้ง่าย: ชุดเมทริกซ์ 3x3 3 เฟรมที่เราต้องการกำหนดให้กับอาร์เรย์ย่อย 2x2 2x2 ด้านซ้ายบนและด้านขวาล่างให้กับสองเฟรมแรกและทั้งหมดไปยังเฟรมสุดท้าย :
multi_array = np.zeros((3, 3, 3))
slice_rrcc = np.array([[0, 2, 0, 2], [1, 3, 1, 3], [0, 3, 0, 3]])
มาหาดัชนีที่ตรงกับแต่ละดัชนีรวมทั้งขนาดและรูปร่าง:
nframes = slice_rrcc.shape[0] # 3
nrows = np.diff(slice_rrcc[:, :2], axis=1).ravel() # [2, 2, 3]
ncols = np.diff(slice_rrcc[:, 2:], axis=1).ravel() # [2, 2, 3]
sizes = nrows * ncols # [4, 4, 9]
เราต้องการดัชนีแฟนซีต่อไปนี้เพื่อให้สามารถทำงานได้:
frame_index = np.array([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2])
row_index = np.array([0, 0, 1, 1, 1, 1, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2])
col_index = np.array([0, 1, 0, 1, 1, 2, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2])
ถ้าเราสามารถได้รับอาร์เรย์frame_index
, row_index
และcol_index
เราสามารถตั้งค่าข้อมูลสำหรับแต่ละกลุ่มดังนี้
multi_array[frame_index, row_index, col_index] = 1
frame_index
ดัชนีหาได้ง่าย:
frame_index = np.repeat(np.arange(nframes), sizes)
row_index
ใช้เวลาทำงานอีกเล็กน้อย คุณต้องสร้างชุดของnrows
ดัชนีสำหรับแต่ละเฟรมและทำซ้ำncols
หลายครั้ง คุณสามารถทำได้โดยสร้างช่วงต่อเนื่องและเริ่มการนับใหม่ในแต่ละเฟรมโดยใช้การลบ:
row_range = np.arange(nrows.sum())
row_offsets = np.zeros_like(row_range)
row_offsets[np.cumsum(nrows[:-1])] = nrows[:-1]
row_index = row_range - np.cumsum(row_offsets) + np.repeat(slice_rrcc[:, 0], nrows)
segments = np.repeat(ncols, nrows)
row_index = np.repeat(row_index, segments)
col_index
จะยังคงไม่สำคัญน้อยกว่า คุณต้องสร้างลำดับสำหรับแต่ละแถวโดยใช้ค่าชดเชยที่ถูกต้องและทำซ้ำเป็นชิ้น ๆ สำหรับแต่ละแถวจากนั้นสำหรับแต่ละเฟรม วิธีการนี้คล้ายrow_index
กับดัชนีแฟนซีเพิ่มเติมเพื่อให้ได้ลำดับที่ถูกต้อง:
col_index_index = np.arange(sizes.sum())
col_index_resets = np.cumsum(segments[:-1])
col_index_offsets = np.zeros_like(col_index_index)
col_index_offsets[col_index_resets] = segments[:-1]
col_index_offsets[np.cumsum(sizes[:-1])] -= ncols[:-1]
col_index_index -= np.cumsum(col_index_offsets)
col_range = np.arange(ncols.sum())
col_offsets = np.zeros_like(col_range)
col_offsets[np.cumsum(ncols[:-1])] = ncols[:-1]
col_index = col_range - np.cumsum(col_offsets) + np.repeat(slice_rrcc[:, 2], ncols)
col_index = col_index[col_index_index]
เมื่อใช้สูตรนี้คุณสามารถเพิ่มขั้นตอนและระบุค่าที่แตกต่างกันสำหรับแต่ละเฟรมได้ หากคุณต้องการกำหนดvalues = [1, 2, 3]
ให้กับเฟรมในตัวอย่างของฉันเพียงแค่ทำ
multi_array[frame_index, row_index, col_index] = np.repeat(values, sizes)
เราจะดูว่ามีวิธีที่มีประสิทธิภาพมากกว่านี้หรือไม่ ส่วนหนึ่งผมถามเกี่ยวกับการเป็นที่นี่
เกณฑ์มาตรฐาน
การเปรียบเทียบลูปของคุณกับโซลูชันเวกเตอร์ของฉันสำหรับnframes
ใน {10, 100, 1000} และความกว้างและความสูงของmulti_array
in {100, 1000, 10000}
:
def set_slices_loop(arr, slice_rrcc):
for a, s in zip(arr, slice_rrcc):
a[s[0]:s[1], s[2]:s[3]] = 1
np.random.seed(0xABCDEF)
for nframes in [10, 100, 1000]:
for dim in [10, 32, 100]:
print(f'Size = {nframes}x{dim}x{dim}')
arr = np.zeros((nframes, dim, dim), dtype=int)
slice = np.zeros((nframes, 4), dtype=int)
slice[:, ::2] = np.random.randint(0, dim - 1, size=(nframes, 2))
slice[:, 1::2] = np.random.randint(slice[:, ::2] + 1, dim, size=(nframes, 2))
%timeit set_slices_loop(arr, slice)
arr[:] = 0
%timeit set_slices(arr, slice)
ผลลัพธ์เป็นที่ชื่นชอบของลูปอย่างท่วมท้นยกเว้นเพียงเฟรมจำนวนมากและขนาดเฟรมที่เล็ก กรณี "ปกติ" ส่วนใหญ่จะเรียงลำดับความสำคัญได้เร็วขึ้นด้วยการวนซ้ำ:
วนลูป
| Dimension |
| 100 | 1000 | 10000 |
--------+---------+---------+---------+
F 10 | 33.8 µs | 35.8 µs | 43.4 µs |
r -----+---------+---------+---------+
a 100 | 310 µs | 331 µs | 401 µs |
m -----+---------+---------+---------+
e 1000 | 3.09 ms | 3.31 ms | 4.27 ms |
--------+---------+---------+---------+
Vectorized
| Dimension |
| 100 | 1000 | 10000 |
--------+---------+---------+---------+
F 10 | 225 µs | 266 µs | 545 µs |
r -----+---------+---------+---------+
a 100 | 312 µs | 627 µs | 4.11 ms |
m -----+---------+---------+---------+
e 1000 | 1.07 ms | 4.63 ms | 48.5 ms |
--------+---------+---------+---------+
TL; ดร
สามารถทำได้ แต่ไม่แนะนำ:
def set_slices(arr, slice_rrcc, value):
nframes = slice_rrcc.shape[0]
nrows = np.diff(slice_rrcc[:, :2], axis=1).ravel()
ncols = np.diff(slice_rrcc[:, 2:], axis=1).ravel()
sizes = nrows * ncols
segments = np.repeat(ncols, nrows)
frame_index = np.repeat(np.arange(nframes), sizes)
row_range = np.arange(nrows.sum())
row_offsets = np.zeros_like(row_range)
row_offsets[np.cumsum(nrows[:-1])] = nrows[:-1]
row_index = row_range - np.cumsum(row_offsets) + np.repeat(slice_rrcc[:, 0], nrows)
row_index = np.repeat(row_index, segments)
col_index_index = np.arange(sizes.sum())
col_index_resets = np.cumsum(segments[:-1])
col_index_offsets = np.zeros_like(col_index_index)
col_index_offsets[col_index_resets] = segments[:-1]
col_index_offsets[np.cumsum(sizes[:-1])] -= ncols[:-1]
col_index_index -= np.cumsum(col_index_offsets)
col_range = np.arange(ncols.sum())
col_offsets = np.zeros_like(col_range)
col_offsets[np.cumsum(ncols[:-1])] = ncols[:-1]
col_index = col_range - np.cumsum(col_offsets) + np.repeat(slice_rrcc[:, 2], ncols)
col_index = col_index[col_index_index]
if values.size == 1:
arr[frame_index, row_index, col_index] = value
else:
arr[frame_index, row_index, col_index] = np.repeat(values, sizes)
นี่คือโพสต์การเปรียบเทียบโดยใช้benchitแพ็กเกจ (เครื่องมือเปรียบเทียบบางส่วนที่รวมเข้าด้วยกันข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้เขียน) เพื่อเปรียบเทียบแนวทางแก้ไข
เรามีการเปรียบเทียบset_slices
จาก soln @Mad ฟิสิกส์ที่มีarr[frame_index, row_index, col_index] = 1
และset_slices_loop
ไม่มีการเปลี่ยนแปลงใด ๆ (sec)
ที่จะได้รับรันไทม์
np.random.seed(0xABCDEF)
in_ = {}
for nframes in [10, 100, 1000]:
for dim in [10, 32, 100]:
arr = np.zeros((nframes, dim, dim), dtype=int)
slice = np.zeros((nframes, 4), dtype=int)
slice[:, ::2] = np.random.randint(0, dim - 1, size=(nframes, 2))
slice[:, 1::2] = np.random.randint(slice[:, ::2] + 1, dim, size=(nframes, 2))
in_[(nframes, dim)] = [arr, slice]
import benchit
funcs = [set_slices, set_slices_loop]
t = benchit.timings(funcs, in_, input_name=['NumFrames', 'Dim'], multivar=True)
t.plot(sp_argID=1, logx=True, save='timings.png')
