2014-09-04 3 views
2

У меня есть 2D-массив numpy, который представляет монохромное изображение с CCD, который был закодирован 3x3 (то есть каждое значение в массиве представляет 9 пикселей (3x3) на физическом CCD).Rescale numpy array

Я хочу перемасштабировать его в соответствии с оригинальной компоновкой ПЗС (чтобы я мог легко наложить его на неизогнутое изображение с той же ПЗС-матрицы).

Я видел Resampling a numpy array representing an image, но это не похоже на то, что я хочу.

Предположим, у меня есть массив г:

import numpy as np 
import scipy.ndimage 

g = np.array([[0, 1, 2], 
       [3, 4, 5], 
       [6, 7, 8]]) 

Когда я пытаюсь масштабировать его в 2 раза:

o = scipy.ndimage.zoom(g, 2, order=0) 

я получаю именно то, что я ожидаю, - каждое значение теперь 2x2 идентичны значения:

array([[0, 0, 1, 1, 2, 2], 
     [0, 0, 1, 1, 2, 2], 
     [3, 3, 4, 4, 5, 5], 
     [3, 3, 4, 4, 5, 5], 
     [6, 6, 7, 7, 8, 8], 
     [6, 6, 7, 7, 8, 8]]) 

Но когда я пытаюсь масштабироваться с коэффициентом 3, я получаю это:

o = scipy.ndimage.zoom(g, 3, order=0) 

дает мне:

array([[0, 0, 1, 1, 1, 1, 2, 2, 2], 
     [0, 0, 1, 1, 1, 1, 2, 2, 2], 
     [3, 3, 4, 4, 4, 4, 5, 5, 5], 
     [3, 3, 4, 4, 4, 4, 5, 5, 5], 
     [3, 3, 4, 4, 4, 4, 5, 5, 5], 
     [3, 3, 4, 4, 4, 4, 5, 5, 5], 
     [6, 6, 7, 7, 7, 7, 8, 8, 8], 
     [6, 6, 7, 7, 7, 7, 8, 8, 8], 
     [6, 6, 7, 7, 7, 7, 8, 8, 8]]) 

Я хотел каждое значение в исходном массиве, чтобы стать набором значений 3х3 ... это не то, что я получаю.

Как я могу это сделать? (И почему я получаю этот неинтуитивный результат?)

+1

Почему -1? Это был плохой вопрос? Слишком много тегов? Будучи новичком SO, я был бы признателен за некоторые отзывы о том, что я делаю неправильно. –

+0

Я предполагаю, что кто-то видел это, знал ответ, предположил, что вы не потрудились прочитать документы на 'zoom', и, следовательно, опустили вас на помощь вампиру. Лично я могу понять, почему вы все еще можете быть смущены после прочтения документов 'zoom', но я думаю, вы могли бы лучше объяснить? Я не знаю. – abarnert

+0

похоже дубликат http://stackoverflow.com/questions/7525214/how-to-scale-a-numpy-array?rq=1 –

ответ

3

Вы можете использовать np.kron:

In [16]: g 
Out[16]: 
array([[0, 1, 2], 
     [3, 4, 5], 
     [6, 7, 8]]) 

In [17]: np.kron(g, np.ones((3,3), dtype=int)) 
Out[17]: 
array([[0, 0, 0, 1, 1, 1, 2, 2, 2], 
     [0, 0, 0, 1, 1, 1, 2, 2, 2], 
     [0, 0, 0, 1, 1, 1, 2, 2, 2], 
     [3, 3, 3, 4, 4, 4, 5, 5, 5], 
     [3, 3, 3, 4, 4, 4, 5, 5, 5], 
     [3, 3, 3, 4, 4, 4, 5, 5, 5], 
     [6, 6, 6, 7, 7, 7, 8, 8, 8], 
     [6, 6, 6, 7, 7, 7, 8, 8, 8], 
     [6, 6, 6, 7, 7, 7, 8, 8, 8]]) 

Выхода zoom(g, 3, order=0) немного удивительно. Рассмотрим первую строку: [0, 0, 1, 1, 1, 1, 2, 2, 2]. Почему есть четыре 1 s?

Когда order=0 увеличение (в действительности) вычисляет np.linspace(0, 2, 9), который выглядит как

In [80]: np.linspace(0, 2, 9) 
Out[80]: array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ]) 

, а затем округляет значения. Если вы используете np.round(), вы получите:

In [71]: np.round(np.linspace(0, 2, 9)).astype(int) 
Out[71]: array([0, 0, 0, 1, 1, 1, 2, 2, 2]) 

Обратите внимание, что np.round(0.5) дает 0, но np.round(1.5) дает 2. np.round() использует "round half to even" tie-breaking rule. Видимо, округление производится в zoom код использует "round half down" rule: она округляет 0.5 к 0 и 1.5 к 1, как в следующем

In [81]: [int(round(x)) for x in np.linspace(0, 2, 9)] 
Out[81]: [0, 0, 1, 1, 1, 1, 2, 2, 2] 

, и поэтому есть четыре 1 s в там.

+0

Я знаю, что я не должен публиковать комментарии «спасибо», но спасибо за добавление объяснения, почему zoom() не делает то, что я ожидал. Теперь это имеет смысл. Я бы дал вам еще один +1, если бы мог ... –

2

И почему я получаю этот непреднамеренный результат?

Потому что zoom является функцией сплайновой интерполяции.Другими словами, он рисует кубический сплайн из середины этого 1 в середину этого 0, а значения между ними получают значения сплайна в соответствующем месте.

Если вам нужна ближайшая, линейная или квадратичная интерполяция вместо кубического, вы можете использовать аргумент order=0 или order=1 или order=2. Но если вы вообще не хотите интерполяции - вы не используете интерполяционную функцию. Это похоже на вопрос, почему использование [int(i*2.3) for i in range(10)] для получения четных чисел от 0 до 20 дает вам некоторые нечетные числа. Это не функция для получения четных чисел от 0 до 20, поэтому она не делает этого, но делает именно то, что вы просили.


Как я могу это сделать?

Опять же, если вы хотите не интерполированное масштабирование, не используйте функцию интерполяции. Простейшим способом, вероятно, является использование np.kron, для Kroenecker - умножить ваш массив на np.ones((scale, scale)).

+0

Hm. Если «ближайший порядок = 0» (по http://stackoverflow.com/questions/13242382/resampling-a-numpy-array-representing-an-image), то результат все еще кажется неинтуитивным. Но у меня есть мой ответ. –

+0

@ nerdfever.com: Да, ближайший порядок = 0. И ближайшее значение в исходном массиве для третьего значения в расширенном массиве - это второе, а не первое. Я собирался объяснить это более подробно, но я только заметил, что Уоррен Уокессер уже проделал большую работу. Жаль, что я уже поддержал его оригинальный ответ, потому что его отредактированная версия заслуживает другого. :) – abarnert

+0

@abanert: Действительно? Я полагаю, вы должны быть верны или они не сделали бы этого так, но, глядя на него одномерным, мы имеем [n1 n2 n3] отображение в [m1 m2 m3 m4 m5 m6 m7 m8 m9]. Я предположил бы (возможно, ошибочно), что n1 отображает m2 и n2 отображает m5. Если это так, то m3 ближе к n1, чем n2, и должно получить значение n1. Если вы не взвешиваете «ближайший» в обоих измерениях как-то. Но я думал, что «ближайший» означает буквально «ближайший», а не «ближайший после взвешивания». [Возможно, я ошибся.] Даже в двух измерениях m3 имеет значение ADJACENT до m2 и не смежна с каким-либо другим отображаемым значением. –