gnuplot: comment tracer un élément de tableau 2D par pixel sans marges

9

J'essaie d'utiliser gnuplot 5.0 pour tracer un tableau 2D de données sans marges, bordures ou axes ... juste une image 2D (.png ou .jpg) représentant certaines données. Je souhaite avoir chaque élément de réseau pour correspondre exactement à un pixel de l'image avec aucune mise à l' échelle / interpolation , etc et pas de pixels blancs supplémentaires sur les bords.

Jusqu'à présent, lorsque j'essaie de définir les marges à 0 et même en utilisant le pixelsdrapeau, je reste avec une rangée de pixels blancs sur les bordures droite et supérieure de l'image.

Comment puis-je obtenir juste un fichier image avec une représentation pixel par pixel d'un tableau de données et rien de plus?

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

Exemple de données de 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

pixels supplémentaires

HotDogCannon
la source
serait-il acceptable si les données sont sous forme de x y zliste?
theozh

Réponses:

5

Certains terminaux gnuplot implémentent "avec image" en créant un fichier png séparé contenant l'image, puis en y liant à l'intérieur du tracé résultant. L'utilisation directe de ce fichier image png séparé évitera tout problème de mise en page, de marges, etc. Ici, j'utilise le terminal canvas. L'intrigue elle-même est jetée; nous ne conservons que le fichier png créé avec le contenu souhaité.

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
Ethan
la source
C'est court et rapide et ça marche! Les seules choses que je n'ai pas encore réussi: 1. éviter la sortie dans Windows, 2. donner mon propre nom sans index au fichier png ou au moins arrêter d'augmenter l'index du fichier png à chaque fois que vous recréez l'intrigue.
theozh
Je ne peux pas aider avec la sortie Windows. Vous avez raison au sujet du compteur dans le terminal de canevas; il ne se réinitialise jamais. Mais vous pouvez jouer le même tour avec set term tikz externalimageset ce terminal réinitialise le compteur à chaque "terme défini". Vous ne pouvez pas l'utiliser set output "/dev/null"avec tikz, mais si cela ne fonctionne pas pour supprimer la sortie pour vous, alors vous ne vous en souciez peut-être pas. Les terminaux tkcanvas et svg sont d'autres possibilités, mais le mécanisme externe de png dépend de la version de gnuplot et des options de compilation.
Ethan
Cela fonctionne très bien! Un petit changement de nom après le fait est nécessaire, mais à la fin, j'obtiens un fichier image au pixel près comme je le cherchais. Merci!
HotDogCannon
3

N'utilisez pas gnuplot.

Au lieu de cela, écrivez un script qui lit vos données et les convertit en l'un des formats Portable Anymap . Voici un exemple en 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()

Si vous pouvez modifier le programme qui génère le fichier de données, vous pouvez même ignorer l'étape ci-dessus et générer à la place les données directement au format PNM.

Dans les deux cas, vous pouvez ensuite utiliser ImageMagick pour convertir l'image au format de votre choix:

./convert.py | convert - pic.png
user3840170
la source
1
En effet, utiliser Gnuplot pour ce genre de tâche, c'est comme utiliser un livre pour enfoncer un clou dans quelque chose. Cela peut fonctionner mais les livres ne sont pas faits pour cette tâche. Mon outil préféré serait GNU Octave pour rester dans le domaine "GNU" :) La imwritefonction peut être utilisée pour enregistrer des données 2D en tant qu'image PNG.
blerontin
C'est un itinéraire intéressant et certainement une sauvegarde précieuse, mais si je ne vais pas l'utiliser gnuplot, je vais simplement l'utiliser à la matplotlibplace (voir ma réponse ci-dessous). C'est une grande contribution, mais techniquement la question / prime demande une gnuplotsolution, aussi impropre qu'elle semble être pour cette tâche particulière.
HotDogCannon
3

Cela devrait être une tâche facile, mais ce n'est apparemment pas le cas. Ce qui suit peut être une solution (lourde) car toutes les autres tentatives ont échoué. Je soupçonne que certaines bibliothèques graphiques ont un problème que vous ne pouvez probablement pas résoudre en tant qu'utilisateur gnuplot.

Vous avez mentionné que les données de matrice ASCII sont également correctes. Le "truc" ici est de tracer des données with linesoù les données sont "interrompues" par des lignes vides, dessinant essentiellement des points uniques. Vérifiez ceci au cas où vous auriez besoin de placer votre fichier de données 1: 1 dans un bloc de données .

Cependant, s'il n'est pas déjà assez étrange, il semble fonctionner pour pnget gifterminal mais pas pour pngcairoou wxt. Je suppose que la solution de contournement est probablement lente et inefficace, mais au moins elle crée la sortie souhaitée. Je ne sais pas s'il y a une limite de taille. Testé avec 100x100 pixels avec Win7, gnuplot 5.2.6. Les commentaires et améliorations sont les bienvenus.

Code:

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

Résultat: (100x100 pixels)

entrez la description de l'image ici

(agrandi avec un fond noir):

entrez la description de l'image ici

Image avec 400x200 pixels (prend environ 22 secondes sur mon ordinateur portable de 8 ans).

entrez la description de l'image ici

theozh
la source
et si SizeX et SizeY ne sont pas égaux?
HotDogCannon
désolé, je me suis mélangé xet y. Cela fonctionne toujours si SizeXet SizeYne sont pas égaux. Je vais corriger le code.
theozh
OK, approche intéressante, mais comment puis-je d'abord lire des données binaires de Fortran vers un tableau ou un flux comme le vôtre Data2?
HotDogCannon
pourriez-vous en quelque sorte fournir un fichier binaire 400x200 avec des données en virgule flottante à tester?
theozh
1

Ce que j'ai finalement utilisé pour obtenir ce dont j'avais besoin, même si la question / prime demande une gnuplotsolution:

matplotliba une fonction matplotlib.pyplot.imsave qui fait ce que je cherchais ... c'est-à-dire tracer "juste des pixels de données" et pas d'extras comme des bordures, des marges, des axes, etc. À l'origine, je ne connaissais que matplotlib.pyplot.imshow et je devais tirer de nombreuses astuces pour éliminer tous les extras du fichier image et empêcher toute interpolation / lissage, etc. (et donc tourné vers gnuplotà un certain point). Avec imsavec'est assez facile, je suis donc de retour à utiliser matplotlibpour une solution simple mais toujours flexible (en termes de palette de couleurs, de mise à l'échelle, etc.) pour les tracés 'pixel exact'. Voici un exemple:

#!/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')
HotDogCannon
la source
1

OK, voici une autre solution possible (je l'ai séparée de ma première approche lourde). Il crée l'intrigue immédiatement, moins d'une seconde. Aucun renommage nécessaire ni création d'un fichier inutile.

Je suppose que la clé est d'utiliser term pnget ps 0.1.

Je n'ai pas de preuve mais je pense que ce ps 1serait ca. 6 pixels de large et créerait un chevauchement et / ou des pixels blancs au coin. Encore une fois, pour une raison quelconque, il semble fonctionner avec term pngmais pas avec term pngcairo.

Ce que j'ai testé (Win7, gnuplot 5.2.6) est un fichier binaire ayant le motif 00 00 FFrépété partout (je ne peux pas afficher ici d'octets nuls). Étant donné que gnuplot lit apparemment 4 octets par élément de tableau ( format="%d"), cela conduit à un motif RVB alternatif si je trace with lc rgb var.

De la même manière (espérons-le), nous pouvons comprendre comment le lire format="%f"et l'utiliser avec une palette de couleurs. Je suppose que c'est ce que vous cherchez, non? D'autres résultats de test, commentaires, améliorations et explications sont les bienvenus.

Code:

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

Résultat:

entrez la description de l'image ici

theozh
la source