gnuplot: come tracciare un elemento array 2D per pixel senza margini


9

Sto cercando di usare gnuplot 5.0 per tracciare un array di dati 2D senza margini o bordi o assi ... solo un'immagine 2D (.png o .jpg) che rappresenta alcuni dati. Vorrei avere ogni elemento dell'array corrispondere esattamente a un pixel nell'immagine con alcuna scalatura / interpolazione etc e nessun pixel bianchi supplementari ai bordi.

Finora, quando provo a impostare i margini su 0 e anche usando la pixelsbandiera, rimango ancora con una fila di pixel bianchi sui bordi destro e superiore dell'immagine.

Come posso ottenere solo un file di immagine con una rappresentazione pixel per pixel di un array di dati e niente di più?

script 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

Dati di esempio da 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

pixel extra


sarebbe accettabile se i dati fossero in x y zformato elenco?
theozh

Risposte:


5

Alcuni terminali gnuplot implementano "con l'immagine" creando un file png separato contenente l'immagine e quindi collegandolo ad esso all'interno del grafico risultante. L'uso diretto di quel file immagine png separato eviterà qualsiasi problema di layout di pagina, margini, ecc. Qui utilizzo il terminale canvas. La trama stessa viene gettata via; tutto ciò che conserviamo è il file png creato con il contenuto desiderato.

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

Questo è breve e veloce e funziona! Le uniche cose che non sono ancora riuscito: 1. evita l'output in Windows, 2. dai il mio nome senza indice al file png o almeno smetti di aumentare l'indice del file png ogni volta che ricrea la trama.
theozh

Non posso fare a meno dell'output di Windows. Hai ragione sul bancone nel terminale di tela; non si ripristina mai. Ma puoi giocare lo stesso trucco con set term tikz externalimagesquel terminale e azzera il contatore ad ogni "termine impostato". Non puoi usarlo set output "/dev/null"con tikz, ma se non funziona per sopprimere l'output per te, allora potrebbe non interessarti. I terminali tkcanvas e svg sono altre possibilità, ma il meccanismo png esterno in questi dipende dalla versione di gnuplot e dalle opzioni di compilazione.
Ethan,

Funziona benissimo! Un po 'di rinomina dopo il fatto è necessario, ma alla fine ottengo un file di immagine esatto pixel come stavo cercando. Grazie!
HotDogCannon

3

Non usare gnuplot.

Invece, scrivi uno script che legge i tuoi dati e li converte in uno dei formati Portable Anymap . Ecco un esempio in 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()

Se puoi modificare il programma che genera il file di dati, puoi anche saltare il passaggio precedente e generare invece i dati direttamente in un formato PNM.

In entrambi i casi, puoi quindi utilizzare ImageMagick per convertire l'immagine in un formato a tua scelta:

./convert.py | convert - pic.png

1
In effetti usare Gnuplot per questo tipo di attività è come usare un libro per guidare un chiodo in qualcosa. Potrebbe funzionare ma i libri non sono fatti per questo compito. Il mio strumento preferito sarebbe GNU Octave per rimanere nel dominio "GNU" :) La imwritefunzione può essere utilizzata per salvare i dati 2D come immagine PNG.
blerontin

Questo è un percorso interessante e sicuramente un backup prezioso, ma se non ho intenzione di usarlo gnuplot, lo userò matplotlibinvece (vedi la mia risposta di seguito). Questo è un grande contributo, ma tecnicamente la domanda / generosità richiede una gnuplotsoluzione, inadatta come sembra essere per questo particolare compito.
HotDogCannon,

3

Questo dovrebbe essere un compito facile, tuttavia, a quanto pare non lo è. Di seguito potrebbe essere una soluzione (ingombrante) perché tutti gli altri tentativi falliti. Il mio sospetto è che alcune librerie grafiche abbiano un problema che probabilmente non è possibile risolvere come utente gnuplot.

Hai detto che anche i dati della matrice ASCII sono ok. Il "trucco" qui è quello di tracciare i dati in with linescui i dati sono "interrotti" da linee vuote, fondamentalmente disegnando singoli punti. Controllare questo nel caso in cui sia necessario inserire il file di dati 1: 1 in un blocco dati .

Tuttavia, se non è già abbastanza strano, sembra funzionare per pnge gifterminale ma non per pngcairoo wxt. Immagino che la soluzione alternativa sia probabilmente lenta e inefficiente, ma almeno crea l'output desiderato. Non sono sicuro che ci sia un limite alle dimensioni. Testato con 100x100 pixel con Win7, gnuplot 5.2.6. Commenti e miglioramenti sono benvenuti.

Codice:

### 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

Risultato: (100x100 pixel)

inserisci qui la descrizione dell'immagine

(ingrandito con sfondo nero):

inserisci qui la descrizione dell'immagine

Immagine con 400x200 pixel (dura circa 22 secondi sul mio laptop di 8 anni).

inserisci qui la descrizione dell'immagine


e se SizeX e SizeY non sono uguali?
HotDogCannon,

scusa, ho confuso xe y. Funziona ancora se SizeXe SizeYnon sono uguali. Correggerò il codice.
theozh

OK, approccio interessante, ma come faccio a leggere prima i dati binari da Fortran su un array o un flusso come il tuo Data2?
HotDogCannon,

potresti in qualche modo fornire un file binario 400x200 con dati in virgola mobile da testare?
theozh

1

Quello che ho effettivamente utilizzato per ottenere ciò di cui avevo bisogno, anche se la domanda / generosità richiede una gnuplotsoluzione:

matplotlibha una funzione matplotlib.pyplot.imsave che fa quello che stavo cercando ... cioè tracciare 'solo pixel di dati' e nessun extra come bordi, margini, assi, ecc. Inizialmente sapevo solo matplotlib.pyplot.imshow e dovevo fare molti trucchi per eliminare tutti gli extra dal file di immagine e impedire qualsiasi interpolazione / smussatura ecc. (e quindi girati gnuplota un certo punto). Con imsaveè abbastanza semplice, quindi sono tornato a utilizzare matplotlibuna soluzione semplice ma ancora flessibile (in termini di mappa dei colori, ridimensionamento, ecc.) Per grafici 'pixel esatti'. Ecco un esempio:

#!/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

OK, ecco un'altra possibile soluzione (l'ho separata dal mio primo approccio ingombrante). Crea immediatamente la trama, meno di un secondo. Non è necessario rinominare o creare un file inutile.

Immagino che la chiave sia usare term pnge ps 0.1.

Non ho una prova ma penso che ps 1sarebbe ca. 6 pixel di larghezza e creerebbe alcuni pixel sovrapposti e / o bianchi all'angolo. Ancora una volta, per qualunque motivo sembra funzionare, term pngma non con term pngcairo.

Quello che ho testato (Win7, gnuplot 5.2.6) è un file binario con lo schema 00 00 FFripetuto dappertutto (qui non posso visualizzare byte nulli). Dal momento che gnuplot apparentemente legge 4 byte per elemento dell'array ( format="%d"), questo porta ad un modello RGB alternato se sto pianificando with lc rgb var.

Allo stesso modo (si spera) possiamo capire come leggerlo format="%f"e usarlo insieme a una tavolozza di colori. Immagino sia quello che stai cercando, giusto? Sono benvenuti ulteriori risultati dei test, commenti, miglioramenti e spiegazioni.

Codice:

### 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

Risultato:

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.