Хитрость заключается в том, чтобы не выполнять итерацию с такой низкой степенью детализации, как у вас, а вместо этого перекладывать большую часть работы на оптимизированные функции numpy. меньших изображений (назовите их «каналами»), каждое из которых содержит данные для одной из позиций в полутоновой сетке.
Отдельный каналЗатем изображения могут быть сгенерированы простым поиском, который в Numpy мы можем просто сделать путем индексации таблицы поиска с изображением в градациях серого (т.е. LUT[image]
).
Таблицы поиска
Допустим, мы определяем «размер плитки» (размер одного полутонового рисунка) и отдельные тоновые плитки следующим образом:
TILE_SIZE = (2, 2) # Rows, Cols
TONES = np.array(
[[0, 0,
0, 0],
[0, 1,
0, 0],
[1, 1,
0, 0],
[1, 1,
0, 1],
[1, 1,
1, 1]]
, dtype=np.uint8) * 255
Сначала используем np.linspace
для расчета соответствия между оттенками серого и индексами тонов. Затем для каждой позиции мы создаем таблицу поиска из определения тонов (для этого используем технику поиска).
def generate_LUTs(tones, tile_size):
num_tones, num_tiles = tones.shape
tile_rows, tile_cols = tile_size
assert(num_tiles == (tile_rows * tile_cols))
# Generate map between grayscale value and tone index
gray_level = np.linspace(0, (num_tones - 1), 256, dtype=np.float32)
tone_map = np.uint8(np.round(gray_level))
# Generate lookup tables for each tile
LUTs = []
for tile in range(num_tiles):
LUTs.append(tones[:,tile][tone_map])
return LUTs
Объединение каналов
Теперь, чтобы объединить каналы вполное выходное изображение.
Первым шагом является reshape
каждого изображения канала, так что оно имеет только один столбец.
Затем мы можем объединить все каналыизображения, которые совместно используют одну и ту же строку полутонового рисунка, используя np.hstack
.
Далее мы изменим результаты, напримерчто они имеют то же количество строк, что и входное изображение (то есть теперь у них будет вдвое больше столбцов).
Мы снова объединяем все измененные изображения, используя np.hstack
.
Наконец, мы изменим результат так, чтобы он имел правильное количество строк (в соответствии с размером плитки), и мы закончили.
В коде (обобщенно для любого размера плитки):
def halftone(image, LUTs, tile_size):
tiles = []
for tile in range(len(LUTs)):
tiles.append(LUTs[tile][image])
image_rows, _ = image.shape
tile_rows, tile_cols = tile_size
merged_rows = []
for row in range(tile_rows):
row_tiles = tiles[row * tile_cols:(row + 1) * tile_cols]
merged_row = np.hstack([row_tile.reshape(-1, 1) for row_tile in row_tiles])
merged_rows.append(merged_row.reshape(image_rows, -1))
return np.hstack(merged_rows).reshape(image_rows * tile_rows, -1)
Пример использования:
LUTs = generate_LUTs(TONES, TILE_SIZE)
binary = halftone(gray, LUTs, TILE_SIZE)
Пример вывода:
Ас плитками 3х3: