😆

Longan Nano の使い方

2024/08/09に公開

概要

SDカードに映像データを書き込んで、Type-Cによる給電で見れるようにする。

注意点

0:03:38:09
10:fps
160x80pxで制作

短くてもいいが映像が止まってしまうことがある。
ループにするには、最後黒で終わること

コマンドプロンプトのメモ

実行するPythonのデータなどは同じ階層にする

1.出力(mac ターミナル)

mp4の動画で行った場合
ffmpeg -i /Users/XXXXX/Desktop/XXXXX/test.mp4 -vf "fps=30" -q:v 2 /Users/XXXXX/Desktop/XXXXX/output_bmp/%d.bmp

1.出力

ffmpeg -i C:\Users\XXXXX\Desktop\Test\test.mov -vcodec bmp C:\Users\XXXXX\Desktop\Test\output_bmp%01d.bmp

2.移動

cd C:\Users\XXXXX\Desktop\Test\output_bmp

.移動先でpythonを実行させる

bmp2hex.pyも使うので同じフォルダに入れる

#!/usr/bin/env python

##@file bmp2hex.py
#  @ingroup util
#    A script for converting a 1-bit bitmap to HEX for use in an Arduino sketch.
#
#    The BMP format is well publicized. The byte order of the actual bitmap is a
#    little unusual. The image is stored bottom to top, left to right. In addition,
#    The pixel rows are rounded to DWORDS which are 4 bytes long. SO, to convert this
#   to left to right, top to bottom, no byte padding. We have to do some calculations
#    as we loop through the rows and bytes of the image. See below for more
#
#    Usage: 
#    >>>bmp2hex.py [-i] [-r] [-n] [-d] [-x] [-w <bytes>] [-b <size-bytes>] <infile> <tablename>
#    
#    @param infile        The file to convert.
#    @param tablename    The name of the table to create
#    @param raw            "-r", bitmap written as raw table [optional]
#    @param invert        "-i", to invert image pixel colors [optional] 
#    @param tablewidth    "-w <bytes>, The number of characters for each row of the output table [optional]
#    @param sizebytes    "-b <bytes>, Bytes = 0, 1, or 2. 0 = auto. 1 = 1-byte for sizes. 2 = 2-byte sizes (big endian) [optional]
#    @param named        "-n", use a names structure [optional]
#    @param double        "-d", use double bytes rather than single ones [optional]
#    @param xbm            "-x", use XBM format (bits reversed in byte) [optional]
#    @param version        "-v", returns version number
#    
#    @author Robert Gallup 2016-02
#
#    Author:    Robert Gallup (bg@robertgallup.com)
#    License:   MIT Opensource License
#
#    Copyright 2016-2018 Robert Gallup 
#

import sys, array, os, textwrap, math, random, argparse

class DEFAULTS(object):
    STRUCTURE_NAME = 'GFXMeta'
    VERSION = '2.3'

def main ():

    # Default parameters
    infile = ""
    tablename = "" 
    tablewidth = 16
    sizebytes = 0
    invert = False
    raw = False
    named = False
    double = False
    xbm = False
    version = False
    k210 = False
    k210bin = False
    change = False

    # Set up parser and handle arguments
    parser = argparse.ArgumentParser()
    # parser.add_argument ("infile", help="The BMP file(s) to convert", type=argparse.FileType('r'), nargs='+', default=['-'])
    parser.add_argument ("infile", help="The BMP file(s) to convert", type=argparse.FileType('r'), nargs='*', default=['-'])
    parser.add_argument ("-r", "--raw", help="Outputs all data in raw table format", action="store_true")
    parser.add_argument ("-k", "--k210", help="Outputs as K210 AI RGB format", action="store_true")
    parser.add_argument ("-kbin", "--k210bin", help="Outputs as K210 RGB565 (pix swap) format", action="store_true")
    parser.add_argument ("-c", "--change", help="change color depth", action="store_true")
    parser.add_argument ("-i", "--invert", help="Inverts bitmap pixels", action="store_true")
    parser.add_argument ("-w", "--width", help="Output table width in hex bytes [default: 16]", type=int)
    parser.add_argument ("-b", "--bytes", help="Byte width of BMP sizes: 0=auto, 1, or 2 (big endian) [default: 0]", type=int)
    parser.add_argument ("-n", "--named", help="Uses named structure (" + DEFAULTS.STRUCTURE_NAME + ") for data", action="store_true")
    parser.add_argument ("-d", "--double", help="Defines data in 'words' rather than bytes", action="store_true")
    parser.add_argument ("-x", "--xbm", help="Uses XBM bit order (low order bit is first pixel of byte)", action="store_true")
    parser.add_argument ("-v", "--version", help="Returns the current bmp2hex version", action="store_true")
    args = parser.parse_args()

    # Required arguments
    infile = args.infile

    # Options
    if args.raw:
        raw = args.raw
    if args.invert:
        invert = args.invert
    if args.width:
        tablewidth = args.width
    if args.bytes:
        sizebytes = args.bytes % 3
    if args.named:
        named = args.named
    if args.double:
        double = args.double
    if args.xbm:
        xbm = args.xbm
    if args.version:
        print ('// bmp2hex version ' + DEFAULTS.VERSION)
    if args.k210:
        k210 = args.k210
    if args.k210bin:
        k210bin = args.k210bin
    if args.change:
        change = args.change
        
    # Output named structure, if requested
    if (named):
        print ('struct ' + DEFAULTS.STRUCTURE_NAME + ' {')
        print ('  unsigned   int width;')
        print ('  unsigned   int height;')
        print ('  unsigned   int bitDepth;')
        print ('             int baseline;')
        print ('  ' + ('uint8_t   *', 'uint16_t  *')[double] + 'pixel_data;')
        print ('};')
        print ('')

    # Do the work
    for f in args.infile:
        if f == '-':
            sys.exit()
        bmp2hex(f.name, tablewidth, sizebytes, invert, raw, named, double, xbm, k210, k210bin, change)

# Utility function. Return a long int from array (little endian)
def getLONG(a, n):
    return (a[n+3] * (2**24)) + (a[n+2] * (2**16)) + (a[n+1] * (2**8)) + (a[n])

# Utility function. Return an int from array (little endian)
def getINT(a, n):
    return ((a[n+1] * (2**8)) + (a[n]))

# Reverses pixels in byte
def reflect(a):
    r = 0
    for i in range(8):
        r <<= 1
        r |= (a & 0x01)
        a >>= 1
    return (r)

# Main conversion function
def bmp2hex(infile, tablewidth, sizebytes, invert, raw, named, double, xbm, k210, k210bin, change):

    # Set the table name to the uppercase root of the file name
    tablename = os.path.splitext(infile)[0].upper()

    # Convert tablewidth to characters from hex bytes
    tablewidth = int(tablewidth) * 6

    # Initilize output buffer
    outstring =  ''
    outR =  ''
    outG =  ''
    outB =  ''

    # Open File
    fin = open(os.path.expanduser(infile), "rb")
    uint8_tstoread = os.path.getsize(os.path.expanduser(infile))
    valuesfromfile = array.array('B')
    try:
        valuesfromfile.fromfile(fin, uint8_tstoread)
    finally:
        fin.close()

    # Get bytes from file
    values=valuesfromfile.tolist()

    # Exit if it's not a Windows BMP
    if ((values[0] != 0x42) or (values[1] != 0x4D)):
        sys.exit ("Error: Unsupported BMP format. Make sure your file is a Windows BMP.")

    # Calculate width, heigth
    dataOffset    = getLONG(values, 10)    # Offset to image data
    pixelWidth  = getLONG(values, 18)    # Width of image
    pixelHeight = getLONG(values, 22)    # Height of image
    bitDepth    = getINT (values, 28)    # Bits per pixel
    dataSize    = getLONG(values, 34)   # Size of raw data

    # Calculate line width in bytes and padded byte width (each row is padded to 4-byte multiples)
    byteWidth    = int(math.ceil(float(pixelWidth * bitDepth)/8.0))
    paddedWidth    = int(math.ceil(float(byteWidth)/4.0)*4.0)

    # For auto (sizebytes = 0), set sizebytes to 1 or 2, depending on size of the bitmap
    if (sizebytes==0):
        if (pixelWidth>255) or (pixelHeight>255):
            sizebytes = 2
            sizebytes = 2
        else:
            sizebytes = 1

    # The invert byte is set based on the invert command line flag (but, the logic is reversed for 1-bit files)
    invertbyte = 0xFF if invert else 0x00
    if (bitDepth == 1):
        invertbyte = invertbyte ^ 0xFF

    # Output the hex table declaration
    # Format depending on "raw" flag
    if k210:
        print("const unsigned char gImage_image[]  __attribute__((aligned(128))) ={")
    elif k210bin:
        #RGB565 bin, swap pixel
        k210bin = True    #dummy
    elif change:
        print("P3\r\n%d %d\r\n255\r\n"%(pixelWidth, pixelHeight))
    else:
        if (raw):
            print ('PROGMEM unsigned char const ' + tablename + ' [] = {')
            if (not (sizebytes%2)):
                print ("{0:#04X}".format((pixelWidth>>8) & 0xFF) + ", " + "{0:#04X}".format(pixelWidth & 0xFF) + ", " + \
                      "{0:#04X}".format((pixelHeight>>8) & 0xFF) + ", " + "{0:#04X}".format(pixelHeight & 0xFF) + ",")
            else:
                print ("{0:#04X}".format(pixelWidth & 0xFF) + ", " + "{0:#04X}".format(pixelHeight & 0xFF) + ",")

        elif (named):
            print ('PROGMEM ' + 'uint8_t const ' + tablename + '_PIXELS[] = {')

        elif (xbm):
            print ('#define ' + tablename + '_width ' + str(pixelWidth))
            print ('#define ' + tablename + '_height ' + str(pixelHeight))
            print ('PROGMEM uint8_t const ' + tablename + '_bits[] = {')

        else:
            print ('PROGMEM const struct {')
            print ('  unsigned int   width;')
            print ('  unsigned int   height;')
            print ('  unsigned int   bitDepth;')
            print ('  uint8_t        pixel_data[{0}];'.format(byteWidth * pixelHeight))
            print ('} ' + tablename + ' = {')
            print ('{0}, {1}, {2}, {{'.format(pixelWidth, pixelHeight, bitDepth))

    # Generate HEX bytes for pixel data in output buffer
    try:
        if k210bin:
            if (byteWidth//3)%2 != 0:
                raise Exception("Invalid width!")
            file = open('bmp.bin','ab')
            for i in range(pixelHeight):
                for j in range (byteWidth//6):
                    ndx = dataOffset + ((pixelHeight-1-i) * paddedWidth) + j*6
                    pix0_b = values[ndx+0] ^ invertbyte
                    pix0_g = values[ndx+1] ^ invertbyte
                    pix0_r = values[ndx+2] ^ invertbyte
                    pix1_b = values[ndx+3] ^ invertbyte
                    pix1_g = values[ndx+4] ^ invertbyte
                    pix1_r = values[ndx+5] ^ invertbyte
                    
                    pix0 = (pix0_b>>3)|((pix0_g>>2)<<5)|((pix0_r>>3)<<(5+6))
                    pix1 = (pix1_b>>3)|((pix1_g>>2)<<5)|((pix1_r>>3)<<(5+6))
                    b0 = pix0&0xff;
                    b1 = pix0>>8;
                    b2 = pix1&0xff;
                    b3 = pix1>>8;
                    bs = bytes([b1, b0, b3, b2])
                    # bs = bytes([b0, b1, b2, b3])

                    # v = values[ndx] ^ invertbyte
                    # if (xbm):
                    #     v = reflect(v)
                    #     # print ("{0:#04x}".format(v))
                    # # outstring += "{0:#04x}".format(v) + ", "
                    # bs = bytes(v)
                    #print("%x %x,"%(pix0,pix1))
                    #print("%x %x %x %x"%(pix1&0xff, pix1>>8, pix0&0xff, pix0>>8))
                    #print("%c%c%c%c"%(pix1&0xff, pix1>>8, pix0&0xff, pix0>>8))
                    #tmp = struct.pack('BBBB',)
                    file.write(bs)
            file.close()
            exit(0)
        elif change:
            for i in range(pixelHeight):
                for j in range (pixelWidth):
                    ndx = dataOffset + ((pixelHeight-1-i) * paddedWidth) + j*3
                    b = (values[ndx+0]>>4)<<4
                    g = (values[ndx+1]>>3)<<3
                    r = (values[ndx+2]>>4)<<4
                    print("%d %d %d"%(r,g,b))
                print("\r\n")
        else:
            for i in range(pixelHeight):
                for j in range (byteWidth):
                    ndx = dataOffset + ((pixelHeight-1-i) * paddedWidth) + j
                    v = values[ndx] ^ invertbyte
                    if (xbm):
                        v = reflect(v)
                        # print ("{0:#04x}".format(v))
                    outstring += "{0:#04x}".format(v) + ", "
                    if (j%3 == 0):  #B
                        outB += "{0:#04x}".format(v) + ", "
                    elif (j%3 == 1): #G
                        outG += "{0:#04x}".format(v) + ", " 
                    else: #R
                        outR += "{0:#04x}".format(v) + ", "
                    
    # Wrap the output buffer. Print. Then, finish.
    finally:
        outstring = textwrap.fill(outstring[:-2], tablewidth)
        outR = textwrap.fill(outR[:-1], tablewidth)
        outG = textwrap.fill(outG[:-1], tablewidth)
        outB = textwrap.fill(outB[:-1], tablewidth)
        if k210:
            print ("  //R")
            print (outR)
            print ("  //G")
            print (outG)
            print ("  //B")
            print (outB)
        else:
            print (outstring)
        
        if k210:
            print ('};')
        else:
            if (named):
                print ('};')
                print (DEFAULTS.STRUCTURE_NAME + ' const ' + tablename + ' = {{{0}, {1}, {2}, 0, '.format(pixelWidth, pixelHeight, bitDepth) + \
                     ('(uint8_t *)', '(uint16_t *)')[double] + tablename + "_PIXELS};\n\n")
            else:
                if (not (raw or xbm)):
                    print (".",end = "")
                # print ("};")


# Only run if launched from commandline
if __name__ == '__main__': main()

書き出ししたデータなどをリネームする場合

(ごくたまにリネームした時に連番が崩れてしまうことがある。)

Python rename.py

import os

dirname = '.'

fileAllName = os.listdir('.')

num = 0
for name in fileAllName:
	print(name)
	if name.endswith(".bmp"):
		newname = str(num) + ".bmp"
		print(newname),
		num += 1
		os.rename(name,newname);

書き出し

Python genhex.py
(そのまえに中のwhile num <= 150:をフレーム数にあわせる)

import os

if os.path.exists('bmp.bin'):
    os.remove('bmp.bin')

num = 0
while num <= 150:
    #macの場合はpython.exeをpython3に書き換え 
    os.system("python.exe bmp2hex.py -kbin {0}.bmp".format(num))
    num += 1

4.binをSDに入れて完成

参考

https://youtu.be/gUi_CR9nub4?si=ytsbkEvhUELm3M92
https://qiita.com/neo3016/items/8b82a271f5f855f58e07
https://longan.sipeed.com/zh/examples/badapple.html

Discussion