gnuplot: Zeichnen eines 2D-Array-Elements pro Pixel ohne Ränder

9

Ich versuche, mit gnuplot 5.0 ein 2D-Datenarray ohne Ränder, Ränder oder Achsen zu zeichnen ... nur ein 2D-Bild (.png oder .jpg), das einige Daten darstellt. I haben möchte jedes Array - Element auf genau ein Pixel in dem Bild zu entsprechen , mit keiner Skalierung / Interpolation usw. und ohne zusätzliche weißen Pixel an den Rand.

Wenn ich bisher versuche, die Ränder auf 0 zu setzen und sogar das pixelsFlag zu verwenden, bleibt mir immer noch eine Reihe weißer Pixel am rechten und oberen Bildrand übrig.

Wie kann ich nur eine Bilddatei mit einer pixelweisen Darstellung eines Datenarrays und nichts extra erhalten?

Gnuplot-Skript:

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

Beispieldaten aus 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

zusätzliche Pixel

HotDogCannon
quelle
Wäre es akzeptabel, wenn die Daten im x y zListenformat vorliegen?
Theozh

Antworten:

5

Einige Gnuplot-Terminals implementieren "mit Bild", indem sie eine separate PNG-Datei erstellen, die das Bild enthält, und dann innerhalb des resultierenden Diagramms eine Verknüpfung dazu herstellen. Durch die direkte Verwendung dieser separaten PNG-Bilddatei werden Probleme mit dem Seitenlayout, den Rändern usw. vermieden. Hier verwende ich das Canvas-Terminal. Das Grundstück selbst wird weggeworfen; Alles, was wir behalten, ist die PNG-Datei, die mit dem gewünschten Inhalt erstellt wurde.

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
quelle
Das ist kurz und schnell und funktioniert! Die einzigen Dinge, die mir noch nicht gelungen sind: 1. Vermeiden Sie die Ausgabe in Windows, 2. Geben Sie der PNG-Datei meinen eigenen Namen ohne Index, oder erhöhen Sie zumindest nicht mehr den Index der PNG-Datei, wenn Sie das Diagramm neu erstellen.
Theozh
Ich kann nicht mit Windows-Ausgabe helfen. Sie haben Recht mit dem Zähler im Canvas-Terminal. es wird nie zurückgesetzt. Sie können jedoch den gleichen Streich spielen, set term tikz externalimagesund dieses Terminal setzt den Zähler bei jedem "festgelegten Term" zurück. Sie können nicht set output "/dev/null"mit tikz verwenden, aber wenn dies nicht funktioniert, um die Ausgabe für Sie zu unterdrücken, ist es Ihnen möglicherweise egal. Die Terminals tkcanvas und svg sind andere Möglichkeiten, aber der externe PNG-Mechanismus in diesen hängt von der Gnuplot-Version und den Kompilierungsoptionen ab.
Ethan
Das funktioniert super! Eine kleine Umbenennung ist notwendig, aber am Ende bekomme ich eine pixelgenaue Bilddatei, wie ich sie gesucht habe. Vielen Dank!
HotDogCannon
3

Verwenden Sie keinen Gnuplot.

Schreiben Sie stattdessen ein Skript, das Ihre Daten liest und in eines der Portable Anymap-Formate konvertiert . Hier ist ein Beispiel 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()

Wenn Sie das Programm ändern können, das die Datendatei generiert, können Sie sogar den obigen Schritt überspringen und stattdessen die Daten direkt in einem PNM-Format generieren.

In beiden Fällen können Sie ImageMagick verwenden, um das Bild in ein Format Ihrer Wahl zu konvertieren:

./convert.py | convert - pic.png
user3840170
quelle
1
In der Tat ist die Verwendung von Gnuplot für diese Art von Aufgabe wie die Verwendung eines Buches, um einen Nagel in etwas zu schlagen. Es könnte funktionieren, aber Bücher sind nicht für diese Aufgabe gemacht. Mein bevorzugtes Werkzeug wäre GNU Octave , um in der "GNU" -Domäne zu bleiben :) Mit dieser imwriteFunktion können 2D-Daten als PNG-Bild gespeichert werden.
Blerontin
Dies ist eine interessante Route und definitiv ein wertvolles Backup, aber wenn ich sie nicht verwenden werde, verwende gnuplotich sie matplotlibstattdessen (siehe meine Antwort unten). Dies ist ein großartiger Beitrag, aber technisch gesehen verlangt die Frage / Prämie nach einer gnuplotLösung, so ungeeignet sie für diese spezielle Aufgabe zu sein scheint.
HotDogCannon
3

Dies sollte eine leichte Aufgabe sein, ist es aber anscheinend nicht. Das Folgende könnte eine (umständliche) Lösung sein, da alle anderen Versuche fehlgeschlagen sind. Mein Verdacht ist, dass einige Grafikbibliotheken ein Problem haben, das Sie als Gnuplot-Benutzer wahrscheinlich nicht lösen können.

Sie haben erwähnt, dass ASCII-Matrixdaten ebenfalls in Ordnung sind. Der "Trick" besteht darin, Daten zu zeichnen, bei with linesdenen die Daten durch leere Linien "unterbrochen" werden, wobei im Grunde einzelne Punkte gezeichnet werden. Überprüfen Sie dies, falls Sie Ihre Datendatei 1: 1 in einen Datenblock übertragen müssen .

Wenn es jedoch nicht schon seltsam genug ist, scheint es für pngund gifterminal zu funktionieren, aber nicht für pngcairooder wxt. Ich denke, die Problemumgehung ist wahrscheinlich langsam und ineffizient, aber zumindest wird die gewünschte Ausgabe erstellt. Ich bin mir nicht sicher, ob es eine Größenbeschränkung gibt. Getestet mit 100x100 Pixel mit Win7, Gnuplot 5.2.6. Kommentare und Verbesserungen sind willkommen.

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

Ergebnis: (100x100 Pixel)

Geben Sie hier die Bildbeschreibung ein

(vergrößert mit schwarzem Hintergrund):

Geben Sie hier die Bildbeschreibung ein

Bild mit 400 x 200 Pixel (dauert auf meinem 8 Jahre alten Laptop ungefähr 22 Sekunden).

Geben Sie hier die Bildbeschreibung ein

theozh
quelle
und was ist, wenn SizeX und SizeY nicht gleich sind?
HotDogCannon
Entschuldigung, ich habe verwechselt xund y. Es funktioniert immer noch, wenn SizeXund SizeYnicht gleich. Ich werde den Code korrigieren.
Theozh
OK, interessanter Ansatz, aber wie lese ich zuerst Binärdaten von Fortran in ein Array oder einen Stream wie Ihren ein Data2?
HotDogCannon
Könnten Sie irgendwie eine 400x200-Binärdatei mit Gleitkommadaten zum Testen bereitstellen?
Theozh
1

Was ich letztendlich tatsächlich verwendet habe, um das zu bekommen, was ich brauchte , obwohl die Frage / das Kopfgeld nach einer gnuplotLösung fragt :

matplotlibhat eine Funktion matplotlib.pyplot.imsave, die genau das tut, wonach ich gesucht habe ... dh "nur Datenpixel " und keine Extras wie Ränder, Ränder, Achsen usw. zeichnen . Ursprünglich wusste ich nur über matplotlib.pyplot.imshow Bescheid und musste Ziehen Sie viele Tricks, um alle Extras aus der Bilddatei zu entfernen und Interpolation / Glättung usw. zu verhindern (und wenden Sie sich daher gnuplotan einen bestimmten Punkt). Da imsavees ziemlich einfach ist, verwende ich wieder matplotlibeine einfache und dennoch flexible Lösung (in Bezug auf Farbkarte, Skalierung usw.) für "pixelgenaue" Diagramme. Hier ist ein Beispiel:

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

OK, hier ist eine andere mögliche Lösung (ich habe sie von meinem ersten umständlichen Ansatz getrennt). Es erstellt die Handlung sofort, weniger als eine Sekunde. Kein Umbenennen oder Erstellen einer nutzlosen Datei erforderlich.

Ich denke, Schlüssel ist zu verwenden term pngund ps 0.1.

Ich habe keinen Beweis, aber ich denke, ps 1wäre ca. 6 Pixel groß und würde einige Überlappungen und / oder weiße Pixel an der Ecke erzeugen. Wiederum, aus welchem ​​Grund auch immer, scheint es zu funktionieren, term pngaber nicht mit term pngcairo.

Was ich getestet habe (Win7, Gnuplot 5.2.6), ist eine Binärdatei mit dem Muster 00 00 FF überall wiederholt wird (ich kann hier keine Null-Bytes anzeigen). Da Gnuplot anscheinend 4 Bytes pro Array-Element ( format="%d") liest , führt dies zu einem alternierenden RGB-Muster, wenn ich plotte with lc rgb var.

Auf die gleiche Weise (hoffentlich) können wir herausfinden, wie man liest format="%f" und zusammen mit einer Farbpalette verwendet. Ich denke, das ist es, wonach du suchst, oder? Weitere Testergebnisse, Kommentare, Verbesserungen und Erklärungen sind willkommen.

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

Ergebnis:

Geben Sie hier die Bildbeschreibung ein

theozh
quelle