gnuplot: як побудувати один елемент двовимірного масиву на піксель без поля


9

Я намагаюся використовувати gnuplot 5.0 для побудови двовимірного масиву даних без поля, меж і осей ... просто 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

Приклад даних із Fortran 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форматі списку?
theozh

Відповіді:


5

Деякі термінали gnuplot реалізують "із зображенням", створюючи окремий png-файл, що містить зображення, а потім посилаються на нього всередині отриманого сюжету. Використання цього окремого файлу зображення png безпосередньо дозволить уникнути будь-яких проблем із компонуванням сторінки, полями тощо. Тут я використовую канву для полотна. Сам сюжет викинутий; Все, що ми зберігаємо, - це 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-файлу щоразу, коли ви відтворюєте сюжет.
theozh

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

Це чудово працює! Трохи перейменування після цього факту потрібно, але врешті-решт я отримую файл зображення, що відповідає точній пікселі, як я шукав. Дякую!
HotDogCannon

3

Не використовуйте gnuplot.

Натомість напишіть скрипт, який читає ваші дані та перетворює їх у один із форматів портативної програми 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.
Блеронтін

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

3

Це має бути легким завданням, однак, мабуть, це не так. Наступне може бути (громіздким) рішенням, оскільки всі інші спроби не вдалися. Я підозрюю, що в деякій графічній бібліотеці є проблема, яку ви, ймовірно, не можете вирішити як користувач gnuplot.

Ви згадали, що дані матриці ASCII також у порядку. Тут "хитрість" полягає в побудові даних, with linesде дані "перериваються" порожніми рядками, в основному малюючи одиничні точки. Перевірте це, якщо вам потрібно отримати файл даних 1: 1 у блок даних .

Однак, якщо це вже не досить дивно, воно, здається, працює pngі для gifтерміналу, але не для pngcairoабо wxt. Я думаю, що рішення може бути повільним і неефективним, але, принаймні, це створює бажаний результат. Я не впевнений, чи є обмеження щодо розміру. Тестується на 100x100 пікселів з 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 пікселів (займає приблизно 22 секунди на моєму 8-річному ноутбуці).

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


а що робити, якщо SizeX і SizeY не рівні?
HotDogCannon

вибачте, я переплутав xі y. Це все ще працює, якщо SizeXі SizeYне рівні. Я виправлю код.
theozh

Гаразд, цікавий підхід, але як я спочатку прочитав у двійкових даних із Fortran до масиву чи потоку, як ваш Data2?
HotDogCannon

ви могли якось надати бінарний файл розміром 400x200 з даними плаваючої крапки для тестування?
theozh

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.