Partial Lunar Eclipse 2019-07-16

While going through my photographs I found a picture series titled “Partial Lunar Eclipse 2019”. Some pictures came out really neat and I wanted to share them with you! These pictures were made at the Keplerwarte Linz.

I forgot to upload them back then… seems like I have to go through some old albums to look for cool stuff 🙂

Pseudo Fractal made using C, Bash and FFMpeg

Small visualizations are always fun to create and watch, right? That’s why I decided last weekend to create a small video using a simple C program using some code to create BMP graphics found on the internet (thank you!). Then, I just used bash to create .. a few .. images and stitched them together using ffmpeg. This blog entry should document what exactly I have done, so if I am interested in this again some day in the future, I can just re-use the scripts, commands, and code I created here. But first, enjoy the video!

Creating the Images

The most important thing about this whole mini-project was creating the images themselves. I wrote a small program in C, that takes a parameter and then prints out one frame on standard output in BMP format. The C program is self-contained in one file and looks like this:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

// https://gist.github.com/guohai/5848088

// for Linux platform, plz make sure the size of data type is correct for BMP
// spec. if you use this on Windows or other platforms, plz pay attention to
// this.
typedef int32_t LONG;
typedef uint8_t BYTE;
typedef uint32_t DWORD;
typedef uint16_t WORD;

// __attribute__((packed)) on non-Intel arch may cause some unexpected error,
// plz be informed.

typedef struct tagBITMAPFILEHEADER {
  WORD bfType;      // 2  /* Magic identifier */
  DWORD bfSize;     // 4  /* File size in bytes */
  WORD bfReserved1; // 2
  WORD bfReserved2; // 2
  DWORD bfOffBits;  // 4 /* Offset to image data, bytes */
} __attribute__((packed)) BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;         // 4 /* Header size in bytes */
  LONG biWidth;         // 4 /* Width of image */
  LONG biHeight;        // 4 /* Height of image */
  WORD biPlanes;        // 2 /* Number of colour planes */
  WORD biBitCount;      // 2 /* Bits per pixel */
  DWORD biCompress;     // 4 /* Compression type */
  DWORD biSizeImage;    // 4 /* Image size in bytes */
  LONG biXPelsPerMeter; // 4
  LONG biYPelsPerMeter; // 4 /* Pixels per meter */
  DWORD biClrUsed;      // 4 /* Number of colours */
  DWORD biClrImportant; // 4 /* Important colours */
} __attribute__((packed)) BITMAPINFOHEADER;

typedef struct {
  BYTE b;
  BYTE g;
  BYTE r;
} RGB_data;

int bmp_generator(int width, int height, unsigned char *data) {
  BITMAPFILEHEADER bmp_head;
  BITMAPINFOHEADER bmp_info;
  int size = width * height * 3;

  bmp_head.bfType = 0x4D42; // 'BM'
  bmp_head.bfSize = size + sizeof(BITMAPFILEHEADER) +
                    sizeof(BITMAPINFOHEADER); // 24 + head + info no quad
  bmp_head.bfReserved1 = bmp_head.bfReserved2 = 0;
  bmp_head.bfOffBits = bmp_head.bfSize - size;
  // finish the initial of head

  bmp_info.biSize = 40;
  bmp_info.biWidth = width;
  bmp_info.biHeight = height;
  bmp_info.biPlanes = 1;
  bmp_info.biBitCount = 24; // bit(s) per pixel, 24 is true color
  bmp_info.biCompress = 0;
  bmp_info.biSizeImage = size;
  bmp_info.biXPelsPerMeter = 0;
  bmp_info.biYPelsPerMeter = 0;
  bmp_info.biClrUsed = 0;
  bmp_info.biClrImportant = 0;
  // finish the initial of infohead;

  // copy the data
  fwrite(&bmp_head, 1, sizeof(BITMAPFILEHEADER), stdout);
  fwrite(&bmp_info, 1, sizeof(BITMAPINFOHEADER), stdout);
  fwrite(data, 1, size, stdout);

  return 1;
}

double func_sins(double rx, double ry) { return sin(1 / rx) + sin(1 / ry); }

void setrgb(RGB_data *d, int w, int h, int x, int y, double scale) {
  d->b = (x / (double)w) * 255;
  d->g = (y / (double)w) * 255;
  d->r = func_sins(x / (double)w / M_PI / (1+scale), y / (double)h / M_PI / (1+scale));
}

int main(int argc, char **argv) {
  int i, j;
  const int w = 2048, h = 2048;

  double scale = 0;

  if(argc == 2) {
    scale = atof(argv[1]);
  }

  RGB_data *buffer = calloc(sizeof(RGB_data), w * h);

  for (i = 0; i < w; i++) {
    for (j = 0; j < h; j++) {
      setrgb(&buffer[i * h + j], w, h, j, i, scale);
    }
  }

  bmp_generator(w, h, (BYTE *)buffer);

  return EXIT_SUCCESS;
}

Using this program, I then created a script which just repeatedly calls frakt and pipes it into ffmpeg. Because build systems are boring, this script also compiles the program by itself. Here is the script:

#! /usr/bin/env bash

export LC_NUMERIC="en_US.UTF-8"

echo "Compile frakt.c"
gcc frakt.c -lm -o frakt;

echo "Start generation of movie..."

rm out.mp4;

{
    for i in $(seq 0.0 0.01 50); do
	./frakt $i 
    done
} | ffmpeg -f image2pipe -framerate 60 -i - -c:v libx264 -vf format=yuv420p out.mp4

This is then executed and voilà – the video is finished!

I hope you enjoyed this bit of recreational programming and maybe you can re-use a bit of the code. Have a nice day!