試してみたブログ

AI関連・iPhone/Pixelなどのガジェット・音声入力・サーマルプリンタなど興味をある事をどんどん試してみた際の記録

Pixelaのグラフをサーマルプリンタから印刷する

背景

  • 昨日のエントリーでPixelaを使ってグラフ化・可視化を行った

tameshitemita.blog

  • やっぱりグラフ化して毎日の記録を見れるのはうれしい
  • 年末だけではなくて、毎月やった方が良さそう
  • 定期的に印刷して眺めたい欲が出て来た

達成するには

  • Pixelaのグラフを画像として取得する
  • 画像形式の変換
  • 横幅576がMaxなので縦に画像を回転させる必要がある

試してみた

画像の取得

-CairoSVGというSVGpngに変換する物があったのでpip install - 単に横に回転させただけだと40cmほどの巨大なレシートグラフになったので、高さを450に指定 - センター寄せ+前後のトリムをかける - 若い芝が見えづらいのでコントラストとシャープネスを調整

from escpos.printer import Serial
from PIL import Image, ImageChops, ImageEnhance
from cairosvg import svg2png
import io
import requests

SVG_URL = "https://pixe.la/v1/users/XXXX/graphs/test-graph.svg"
TARGET_WIDTH = 450          # 回転後の横幅
PRINTER_WIDTH = 576         # 用紙幅(プリンタのドット幅)

def fetch_svg(url: str) -> str:
    r = requests.get(url, timeout=10)
    r.raise_for_status()
    return r.text

def svg_text_to_image(svg_text: str) -> Image.Image:
    png_bytes = svg2png(bytestring=svg_text.encode("utf-8"))
    img = Image.open(io.BytesIO(png_bytes))
    img.load()
    return img.convert("L")

def resize_by_width(img: Image.Image, target_w: int) -> Image.Image:
    w, h = img.size
    if w == target_w:
        return img
    ratio = target_w / float(w)
    new_w = target_w
    new_h = int(h * ratio)
    return img.resize((new_w, new_h))

def trim_border(img: Image.Image) -> Image.Image:
    bg_color = img.getpixel((0, 0))
    bg = Image.new(img.mode, img.size, bg_color)
    diff = ImageChops.difference(img, bg)
    bbox = diff.getbbox()
    if bbox:
        return img.crop(bbox)
    return img

def enhance_image(img: Image.Image) -> Image.Image:
    contrast_factor = 1.8
    sharpness_factor = 1.5
    img = ImageEnhance.Contrast(img).enhance(contrast_factor)
    img = ImageEnhance.Sharpness(img).enhance(sharpness_factor)
    return img

def center_on_paper(img: Image.Image, paper_width: int) -> Image.Image:
    """
    横幅 paper_width のキャンバスを作り、img を中央に貼る
    """
    w, h = img.size
    if w >= paper_width:
        # すでに紙幅以上ならそのまま(もしくは縮小を検討)
        return img

    left = (paper_width - w) // 2
    canvas = Image.new(img.mode, (paper_width, h), 255)  # 背景は白
    canvas.paste(img, (left, 0))
    return canvas

def main():
    svg_text = fetch_svg(SVG_URL)
    img = svg_text_to_image(svg_text)

    # 1. 回転
    img = img.rotate(90, expand=True)

    # 2. 幅450にリサイズ
    img = resize_by_width(img, TARGET_WIDTH)

    # 3. 余白トリム(縦の無駄を削る)
    img = trim_border(img)

    # 4. コントラスト・シャープネス調整
    img = enhance_image(img)

    # 5. 用紙幅576pxの中央に配置
    img = center_on_paper(img, PRINTER_WIDTH)

    # 6. 印刷
    p = Serial(devfile="/dev/ttyACM0", baudrate=9600)
    p.image(img)
    p.cut()

if __name__ == "__main__":
    main()

iPhone/iPadからの呼出

-ショートカットアプリで下記を作成

出力!

振り返り

白黒でも十分芝の生え具合が確認できた!最終的には30cmのレシートになったが、幅がワンサイズ小さい感熱紙でやると上下の余白が小さくなり横も短くなりそう。

ホワイトボードに貼って今年から頑張っている姿を見るとうれしい!今回ボルダリングの行った回数のグラフにしたが、いろんな毎日の積み重ねの記録に使えそう。いろいろ試していく!!