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 voudrais que chaque élément du tableau corresponde exactement à un pixel de l'image avec sans 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 drapeau pixels
, il me reste encore 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
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
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 ASCII sont également correctes. L'astuce consiste ici à tracer les données with lines
où les données sont "interrompues" par des lignes vides, dessinant essentiellement des points uniques. Vérifiez cela 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 le terminal png
et gif
mais pas pour pngcairo
ou 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)
(agrandi avec un fond noir):
Image avec 400x200 pixels (prend environ 22 secondes sur mon ordinateur portable de 8 ans).
N'utilisez pas gnuplot.
Au lieu de cela, écrivez un script qui lit vos données et les convertit en l'un des formats Anymap portables . 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 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
Ce que j'ai finalement utilisé pour obtenir ce dont j'avais besoin même si la question/prime demande une solution gnuplot
:
matplotlib
a une fonction matplotlib.pyplot.imsave qui fait ce que je cherchais ... c'est-à-dire en traçant 'juste des pixels de données' et aucun extras comme les bordures, marges, axes, etc. À l'origine, je ne connaissais que matplotlib.pyplot.imshow et j'ai dû tirer beaucoup d'astuces pour éliminer tous les extras du fichier image et empêcher toute interpolation/lissage, etc. (et je me suis donc tourné vers gnuplot
à un certain point). Avec imsave
, c'est assez facile, donc je recommence à utiliser matplotlib
pour 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')
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 png
et ps 0.1
.
Je n'ai pas de preuve mais je pense que ps 1
serait 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 png
mais pas avec term pngcairo
.
Ce que j'ai testé (Win7, gnuplot 5.2.6) est un fichier binaire ayant le modèle 00 00 FF
répété partout (je ne peux pas afficher ici d'octets nuls). Puisque 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 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: