gnuplot: как построить один элемент 2D-массива на пиксель без полей


9

Я пытаюсь использовать gnuplot 5.0 для построения 2D-массива данных без полей, границ или осей ... просто 2D-изображение (.png или .jpg), представляющее некоторые данные. Я хотел бы иметь каждый элемент массива соответствует ровно один пиксель изображения с не масштабирования / интерполяции и т.д. , и без каких - либо дополнительных белых пикселей по краям.

До сих пор, когда я пытаюсь установить поля равными 0 и даже используя pixelsфлаг, у меня все еще остается ряд белых пикселей справа и сверху границ изображения.

Как я могу получить только файл изображения с попиксельным представлением массива данных и ничего лишнего?

скрипт gnuplot:

#!/usr/bin/gnuplot --persist

set terminal png size 400, 200

set size ratio -1
set lmargin at screen 0
set rmargin at screen 1
set tmargin at screen 0
set bmargin at screen 1

unset colorbox
unset tics
unset xtics
unset ytics
unset border
unset key

set output "pic.png"

plot "T.dat" binary array=400x200 format="%f" with image pixels notitle

Пример данных из Фортрана 90:

program main
implicit none
integer, parameter :: nx = 400
integer, parameter :: ny = 200
real, dimension (:,:), allocatable :: T
allocate (T(nx,ny))

T(:,:)=0.500
T(2,2)=5.
T(nx-1,ny-1)=5.
T(2,ny-1)=5.
T(nx-1,2)=5.

open(3, file="T.dat", access="stream")
write(3) T(:,:)
close(3)

end program main

дополнительные пиксели


было бы приемлемо, если данные в x y zформате списка?
Теож

Ответы:


5

Некоторые терминалы gnuplot реализуют «с изображением» путем создания отдельного файла png, содержащего изображение, а затем ссылки на него внутри полученного графика. Непосредственное использование этого отдельного файла изображения png позволит избежать проблем с макетом страницы, полями и т. Д. Здесь я использую терминал canvas. Сам сюжет выброшен; все, что мы храним - это файл png, созданный с нужным содержимым.

gnuplot> set term canvas name 'myplot'
Terminal type is now 'canvas'
Options are ' rounded size 600,400 enhanced fsize 10 lw 1 fontscale 1 standalone'
gnuplot> set output '/dev/null'
gnuplot> plot "T.dat" binary array=400x200 format="%f" with image 
   linking image 1 to external file myplot_image_01.png
gnuplot> quit

$identify myplot_image_01.png
myplot_image_01.png PNG 400x200 400x200+0+0 8-bit sRGB 348B 0.000u 0:00.000

Это коротко и быстро и работает! Единственные вещи, которые мне пока не удалось: 1. избежать вывода в Windows, 2. дать свое имя без индекса для png-файла или, по крайней мере, прекратить увеличивать индекс png-файла каждый раз, когда вы воссоздаете график.
Теож

Я не могу помочь с выходом Windows. Вы правы насчет счетчика в терминале холста; он никогда не сбрасывается. Но вы можете сыграть один set term tikz externalimagesи тот же трюк, и этот терминал сбрасывает счетчик при каждом «заданном сроке». Вы не можете использовать set output "/dev/null"с tikz, но если это не сработает, чтобы подавить вывод для вас, то вам может быть все равно. Терминалы tkcanvas и svg - это другие возможности, но внешний механизм png зависит от версии gnuplot и параметров компиляции.
Итан

Это прекрасно работает! Немного переименования по факту необходимо, но в конце я получаю файл изображения с точностью до пикселя, как я искал. Спасибо!
HotDogCannon

3

Не используйте gnuplot.

Вместо этого напишите скрипт, который читает ваши данные и конвертирует их в один из форматов Portable Anymap . Вот пример в Python:

#!/usr/bin/env python3
import math
import struct

width = 400
height = 200
levels = 255

raw_datum_fmt = '=d' # native, binary double-precision float
raw_datum_size = struct.calcsize(raw_datum_fmt)

with open('T.dat', 'rb') as f:
    print("P2")
    print("{} {}".format(width, height))
    print("{}".format(levels))

    raw_data = f.read(width * height * raw_datum_size)

    for y in range(height):
        for x in range(width):
            raw_datum, = struct.unpack_from(raw_datum_fmt, raw_data, (y * width + x) * raw_datum_size)
            datum = math.floor(raw_datum * levels) # assume a number in the range [0, 1]
            print("{:>3} ".format(datum), end='')
        print()

Если вы можете изменить программу, которая генерирует файл данных, вы можете даже пропустить вышеуказанный шаг и вместо этого сгенерировать данные непосредственно в формате PNM.

В любом случае, вы можете использовать ImageMagick для преобразования изображения в формат по вашему выбору:

./convert.py | convert - pic.png

1
На самом деле, использование Gnuplot для такого рода задач похоже на использование книги для забивания гвоздя. Это может работать, но книги не созданы для этой задачи. Моим предпочтительным инструментом будет оставаться GNU Octave в домене «GNU» :) Эта imwriteфункция может использоваться для сохранения 2D-данных в виде изображения PNG.
blerontin

Это интересный маршрут и определенно ценная резервная копия, но если я не собираюсь использовать gnuplot, то я просто буду использовать matplotlibвместо этого (см. Мой ответ ниже). Это большой вклад, но технически вопрос / щедрость требует gnuplotрешения, столь же непригодного, как кажется, для этой конкретной задачи.
HotDogCannon

3

Это должно быть легкой задачей, однако, по-видимому, это не так. Следующее может быть (громоздким) решением, потому что все другие попытки потерпели неудачу. Я подозреваю, что в некоторой графической библиотеке есть проблема, которую вы, вероятно, не можете решить как пользователь gnuplot.

Вы упомянули, что данные матрицы ASCII тоже в порядке. «Хитрость» здесь в том, чтобы построить данные, with linesгде данные «прерываются» пустыми линиями, в основном рисуя отдельные точки. Отметьте это на тот случай, если вам нужно поместить ваш файл данных 1: 1 в блок данных .

Однако, если это не так уже достаточно странно, кажется, работают для pngи gifтерминала , но не для pngcairoили wxt. Я думаю, что обходной путь, вероятно, медленный и неэффективный, но по крайней мере он создает желаемый результат. Я не уверен, есть ли ограничение по размеру. Протестировано с разрешением 100х100 точек на Win7, gnuplot 5.2.6. Комментарии и улучшения приветствуются.

Код:

### pixel image from matrix data without strange white border
reset session

SizeX = 100
SizeY = 100
set terminal png size SizeX,SizeY
set output "tbPixelImage.png"

# generate some random matrix data
set print $Data2
    do for [y=1:SizeY] {
        Line = ''
        do for [x=1:SizeX] {
            Line = Line.sprintf(" %9d",int(rand(0)*0x01000000))  # random color
        }
        print Line
    }
set print
# print $Data2

# convert matrix data into x y z data with empty lines inbetween
set print $Data3
    do for [y=1:SizeY] {
        do for [x=1:SizeX] {
            print sprintf("%g %g %s", x, y, word($Data2[y],x))
            print ""
        }
    }
set print
# print $Data3

set margins 0,0,0,0
unset colorbox
unset border
unset key
unset tics

set xrange[1:SizeX]
set yrange[1:SizeY]

plot $Data3 u 1:2:3 w l lw 1 lc rgb var notitle

set output
### end of code

Результат: (100x100 пикселей)

введите описание изображения здесь

(увеличено на черном фоне):

введите описание изображения здесь

Изображение с разрешением 400x200 пикселей (на моем 8-летнем ноутбуке это занимает около 22 секунд).

введите описание изображения здесь


а что если SizeX и SizeY не равны?
HotDogCannon

извините, я перепутал xи y. Это все еще работает, если SizeXи SizeYне равны. Я исправлю код.
theozh

Хорошо, интересный подход, но как мне сначала прочитать двоичные данные с Фортрана в массив или поток, подобный вашему Data2?
HotDogCannon

Вы могли бы как-то предоставить двоичный файл 400x200 с данными с плавающей запятой для тестирования?
Теож

1

То, что я на самом деле использовал, чтобы получить то, что мне было нужно, хотя вопрос / награда требует gnuplotрешения:

matplotlibимеет функцию matplotlib.pyplot.imsave, которая делает то, что я искал ... то есть вывод «просто пикселей данных» и никаких дополнительных функций, таких как границы, поля, оси и т. д. Изначально я знал только о matplotlib.pyplot.imshow и должен был тянуть много трюков , чтобы устранить все дополнения из файла изображения и предотвратить любые интерполяции / сглаживание и т.д. (и поэтому обратились к gnuplotв определенной точке). С imsaveдовольно легко, поэтому я вернулся к использованию matplotlibдля простого , но по- прежнему гибких (с точкой зрения палитры, масштабирования и т.д.) решений для «пиксела точного» участков. Вот пример:

#!/usr/bin/env python3

import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

nx = 400
ny = 200

data = np.fromfile('T.dat', dtype=np.float32, count=nx*ny)
data = data.reshape((nx,ny), order='F')
matplotlib.image.imsave('T.png', np.transpose(data), origin='lower', format='png')

1

Хорошо, вот другое возможное решение (я отделил его от моего первого громоздкого подхода). Это создает сюжет сразу, менее чем за секунду. Нет необходимости переименования или создания бесполезного файла.

Я думаю, ключом является использование term pngи ps 0.1.

У меня нет доказательств, но я думаю, что ps 1было бы ок. 6 пикселей большого размера и создаст некоторое перекрытие и / или белые пиксели в углу. Опять же, по какой-то причине он работает, term pngно не работает term pngcairo.

Я протестировал (Win7, gnuplot 5.2.6) бинарный файл с 00 00 FFповторяющимся шаблоном (здесь я не могу отображать нулевые байты). Поскольку gnuplot, по-видимому, читает 4 байта на элемент массива ( format="%d"), это приводит к чередующемуся шаблону RGB, если я строю график with lc rgb var.

Таким же образом (надеюсь) мы можем понять, как читать format="%f"и использовать его вместе с цветовой палитрой. Я думаю, это то, что вы ищете, верно? Дальнейшие результаты испытаний, комментарии, улучшения и объяснения приветствуются.

Код:

### pixel image from matrix data without strange white border
reset session

SizeX = 400
SizeY = 200
set terminal png size SizeX,SizeY
set output "tbPixelImage.png"

set margins 0,0,0,0
unset colorbox
unset border
unset key
unset tics

set xrange[0:SizeX-1]
set yrange[0:SizeY-1]

plot "tbBinary.dat" binary array=(SizeX,SizeY) format="%d" w p pt 5 ps 0.1 lc rgb var
### end of code

Результат:

введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.