gnuplot: cách vẽ một phần tử mảng 2D trên mỗi pixel không có lề


9

Tôi đang cố gắng sử dụng gnuplot 5.0 để vẽ một mảng dữ liệu 2D không có lề hoặc viền hoặc trục ... chỉ là hình ảnh 2D (.png hoặc .jpg) đại diện cho một số dữ liệu. Tôi muốn có mỗi phần tử mảng tương ứng với chính xác một pixel trong ảnhkhông có tỷ lệ / nội suy v.v. và không có thêm các pixel trắng ở các cạnh.

Cho đến nay, khi tôi cố gắng đặt lề thành 0 và thậm chí sử dụng pixelscờ, tôi vẫn bị bỏ lại với một hàng pixel trắng ở viền bên phải và trên cùng của hình ảnh.

Làm cách nào tôi có thể nhận được chỉ một tệp hình ảnh với biểu diễn pixel-by-pixel của một mảng dữ liệu và không có gì thêm?

tập lệnh 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

Dữ liệu mẫu từ 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 thêm


nó có thể được chấp nhận nếu dữ liệu ở x y zđịnh dạng danh sách?
theozh

Câu trả lời:


5

Một số thiết bị đầu cuối gnuplot thực hiện "với hình ảnh" bằng cách tạo một tệp png riêng có chứa hình ảnh và sau đó liên kết với nó bên trong âm mưu kết quả. Sử dụng trực tiếp tệp hình ảnh png riêng biệt đó sẽ tránh mọi vấn đề về bố cục trang, lề, v.v ... Ở đây tôi sử dụng thiết bị đầu cuối canvas. Cốt truyện bị vứt bỏ; tất cả những gì chúng tôi giữ là tệp png được tạo với nội dung mong muốn.

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

Đây là ngắn và nhanh chóng và hoạt động! Điều duy nhất tôi chưa thành công: 1. tránh đầu ra trong Windows, 2. đặt tên riêng mà không có chỉ mục cho tệp png hoặc ít nhất là ngừng tăng chỉ mục của tệp png mỗi khi bạn tạo lại âm mưu.
theozh

Tôi không thể giúp với đầu ra Windows. Bạn đã đúng về bộ đếm trong thiết bị đầu cuối vải; nó không bao giờ đặt lại. Nhưng bạn có thể chơi cùng một mẹo với set term tikz externalimagesvà thiết bị đầu cuối đó đặt lại bộ đếm trên mỗi "thời hạn đã đặt". Bạn không thể sử dụng set output "/dev/null"với tikz, nhưng nếu điều đó không hiệu quả để triệt tiêu đầu ra cho bạn thì bạn có thể không quan tâm. Các thiết bị đầu cuối tkcanvas và svg là những khả năng khác, nhưng cơ chế png bên ngoài trong đó phụ thuộc vào phiên bản gnuplot và các tùy chọn biên dịch.
Ethan

Điều này làm việc tuyệt vời! Một chút đổi tên sau khi thực tế là cần thiết, nhưng cuối cùng tôi nhận được một tệp hình ảnh chính xác pixel như tôi đang tìm kiếm. Cảm ơn!
HotDogCannon

3

Đừng sử dụng gnuplot.

Thay vào đó, hãy viết một tập lệnh đọc dữ liệu của bạn và chuyển đổi nó thành một trong các định dạng Anymap di động . Đây là một ví dụ trong 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()

Nếu bạn có thể sửa đổi chương trình tạo tệp dữ liệu, bạn thậm chí có thể bỏ qua bước trên và thay vào đó tạo dữ liệu trực tiếp theo định dạng PNM.

Dù bằng cách nào, bạn có thể sử dụng ImageMagick để chuyển đổi hình ảnh sang định dạng bạn chọn:

./convert.py | convert - pic.png

1
Thật vậy, sử dụng Gnuplot cho loại nhiệm vụ này cũng giống như sử dụng một cuốn sách để đóng đinh vào một thứ gì đó. Nó có thể hoạt động nhưng sách không được thực hiện cho nhiệm vụ này. Công cụ ưa thích của tôi sẽ là GNU Octave để duy trì trong miền "GNU" :) imwriteChức năng này có thể được sử dụng để lưu dữ liệu 2D dưới dạng hình ảnh PNG.
ngừng

Đây là một tuyến thú vị và chắc chắn là một bản sao lưu có giá trị, nhưng nếu tôi sẽ không sử dụng gnuplot, thì tôi sẽ chỉ sử dụng matplotlibthay thế (xem câu trả lời của tôi dưới đây). Đây là một đóng góp lớn, nhưng về mặt kỹ thuật, câu hỏi / tiền thưởng yêu cầu một gnuplotgiải pháp, không phù hợp như nhiệm vụ cụ thể này.
HotDogCannon

3

Đây có thể là một nhiệm vụ dễ dàng, tuy nhiên, dường như không phải vậy. Sau đây có thể là một giải pháp (cồng kềnh) vì tất cả các nỗ lực khác đều thất bại. Sự nghi ngờ của tôi là một số thư viện đồ họa có một vấn đề mà bạn có thể không thể giải quyết như một người dùng gnuplot.

Bạn đã đề cập rằng dữ liệu ma trận ASCII cũng ok. "Bí quyết" ở đây là vẽ dữ liệu with linestrong đó dữ liệu bị "gián đoạn" bởi các dòng trống, về cơ bản là vẽ các điểm đơn lẻ. Kiểm tra điều này trong trường hợp bạn cần đưa tệp dữ liệu 1: 1 của mình vào một cơ sở dữ liệu .

Tuy nhiên, nếu nó chưa đủ lạ, nó dường như hoạt động cho pnggifthiết bị đầu cuối nhưng không phải cho pngcairohoặc wxt. Tôi đoán cách giải quyết có thể chậm và không hiệu quả nhưng ít nhất nó tạo ra đầu ra mong muốn. Tôi không chắc chắn nếu có giới hạn về kích thước. Đã thử nghiệm với 100x100 pixel với Win7, gnuplot 5.2.6. Bình luận và cải tiến đều được chào đón.

Mã số:

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

Kết quả: (100x100 pixel)

nhập mô tả hình ảnh ở đây

(phóng to với nền đen):

nhập mô tả hình ảnh ở đây

Hình ảnh với 400x200 pixel (mất khoảng 22 giây trên máy tính xách tay 8 tuổi của tôi).

nhập mô tả hình ảnh ở đây


và nếu SizeX và SizeY không bằng nhau thì sao?
HotDogCannon

xin lỗi, tôi lẫn lộn xy. Nó vẫn hoạt động nếu SizeXSizeYkhông bằng nhau. Tôi sẽ sửa mã.
theozh

OK, cách tiếp cận thú vị, nhưng lần đầu tiên tôi đọc dữ liệu nhị phân từ Fortran đến một mảng hoặc luồng như của bạn như thế Data2nào?
HotDogCannon

bạn có thể bằng cách nào đó cung cấp một tệp nhị phân 400x200 với dữ liệu dấu phẩy động để kiểm tra?
theozh

1

Những gì tôi đã thực sự sử dụng để có được những gì tôi cần mặc dù câu hỏi / tiền thưởng yêu cầu một gnuplotgiải pháp:

matplotlibcó chức năng matplotlib.pyplot.imsave thực hiện những gì tôi đang tìm kiếm ... tức là vẽ 'chỉ các pixel dữ liệu' và không có phần bổ sung nào như viền, lề, trục, v.v. Ban đầu tôi chỉ biết về matplotlib.pyplot.imshow và phải kéo rất nhiều thủ thuật để loại bỏ tất cả các tính năng bổ sung khỏi tệp hình ảnh và ngăn chặn mọi phép nội suy / làm mịn, v.v. (và do đó chuyển sang gnuplotmột điểm nhất định). Với imsavenó khá dễ dàng, vì vậy tôi quay lại sử dụng matplotlibcho một giải pháp dễ dàng nhưng vẫn linh hoạt (về colormap, chia tỷ lệ, v.v.) cho các ô 'chính xác pixel'. Đây là một ví dụ:

#!/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, đây là một giải pháp khả thi khác (tôi tách nó ra khỏi cách tiếp cận rườm rà đầu tiên của tôi). Nó tạo ra cốt truyện ngay lập tức, chưa đầy một giây. Không cần đổi tên hoặc tạo một tập tin vô dụng.

Tôi đoán chìa khóa là để sử dụng term pngps 0.1.

Tôi không có bằng chứng nhưng tôi nghĩ ps 1sẽ là ca. 6 pixel lớn và sẽ tạo ra một số pixel chồng chéo và / hoặc trắng ở góc. Một lần nữa, vì lý do gì nó dường như làm việc với term pngnhưng không phải với term pngcairo.

Những gì tôi đã kiểm tra (Win7, gnuplot 5.2.6) là một tệp nhị phân có mẫu 00 00 FFlặp đi lặp lại (Tôi không thể hiển thị byte rỗng ở đây). Vì gnuplot rõ ràng đọc 4 byte cho mỗi mục mảng ( format="%d"), điều này dẫn đến một mẫu RGB xen kẽ nếu tôi đang vẽ with lc rgb var.

Theo cùng một cách (hy vọng) chúng ta có thể tìm ra cách đọc format="%f"và sử dụng nó cùng với một bảng màu. Tôi đoán đó là những gì bạn đang tìm kiếm, phải không? Kết quả kiểm tra, nhận xét, cải tiến và giải thích thêm được hoan nghênh.

Mã số:

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

Kết quả:

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.