gnuplot:如何为每个像素绘制一个2D数组元素且无边距


9

我正在尝试使用gnuplot 5.0绘制2D数据数组,没有边界,边框或轴...只是代表某些数据的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'19

Answers:


5

一些gnuplot终端通过创建一个包含图像的单独png文件,然后将其链接到结果图内来实现“带有图像”。直接使用该单独的png图像文件将避免任何页面布局,页边距等问题。在这里,我使用canvas终端。情节本身被扔掉了。我们保留的只是使用所需内容创建的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文件的索引。
theozh19年

我帮不上Windows的输出。您对画布终端中的柜台是正确的;它永远不会重置。但是您可以使用相同的技巧,set term tikz externalimages并且终端会在每个“设置项”上重置计数器。您不能set output "/dev/null"与tikz一起使用,但是如果这不能为您抑制输出,那么您可能不在乎。tkcanvas和svg终端是其他可能性,但是其中的外部png机制取决于gnuplot版本和编译选项。
伊桑(Ethan)

这很棒!在此事实之后需要重命名,但是最后我得到了我一直在寻找的像素精确的图像文件。谢谢!
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”域中的GNU Octave:)该imwrite功能可用于将2D数据另存为PNG图像。
blerontin

这是一条有趣的路线,并且绝对是一笔宝贵的备份,但是如果我不打算使用gnuplot,那么我只会使用它matplotlib(请参阅下面的答案)。这是一个巨大的贡献,但是从技术上讲,问题/悬赏金要求gnuplot解决方案,尽管看起来不适合该特定任务。
HotDogCannon

3

这应该是一个容易的任务,但是显然并非如此。以下可能是一个(麻烦的)解决方案,因为所有其他尝试都失败了。我怀疑某些图形库存在一个问题,您可能无法以gnuplot用户的身份解决。

您提到ASCII矩阵数据也可以。这里的“技巧”是绘制数据with lines,其中数据被空线“打断”,基本上画出了单点。如果您需要将数据文件1:1放入数据块,请选中此复选框。

但是,如果它还不够奇怪,它似乎适用于pnggif终端,但不适用于pngcairowxt。我猜这种解决方法可能很慢且效率低下,但至少它可以创建所需的输出。我不确定大小是否有限制。使用gnuplot 5.2.6在Win7中以100x100像素进行了测试。欢迎评论和改进。

码:

### 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像素的图像(在我8岁的笔记本电脑上大约需要22秒)。

在此处输入图片说明


如果SizeX和SizeY不相等怎么办?
HotDogCannon

对不起,我x和混在一起y。如果SizeXSizeY不相等,它仍在工作。我将更正代码。
–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 pngps 0.1

我没有证据,但我想ps 1大约是。6像素大,会在角落产生一些重叠和/或白色像素。同样,无论出于何种原因,它似乎都可以使用,term png但不能使用term pngcairo

我测试的内容(Win7,gnuplot 5.2.6)是一个二进制文件,其模式00 00 FF全部重复(我在这里不能显示空字节)。由于gnuplot显然每个数组项目(format="%d")读取4个字节,因此如果我进行绘制,这将导致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

结果:

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.