Background

When I bought my hallway lamp a few years ago, I fitted it with energy saving light bulbs. Little did I know that those specific bulbs turned on really slow. I’ve always felt that it took ages before the light output gets close to its final value, and finally I decided to actually measure it as well.

Presenting the result

There are a few different ways to present the results.

  • One would be to simply play back the 20 minute video, but with a large speedup.
  • Another would be to simply show the intensity vs time plot
  • My approach was to combine both of them.

Combining both the video frame, and an animated plot:

Measurement setup

I used a Logitech C615 web camera. It has the ability to lock all settings to manual mode, so the camera won’t adapt to changes in the environment. That meant that I could set it up for a decent image after the hallway lamp had been running for an extended period of time, turn the lamp off for an hour, and then record a video where one would see how the hallway lamp slowly turns on.

Analyzing the video

I started by extracting one frame every second from the video file using ffmpeg

ffmpeg -i /media/slow_hall_lamp.mp4 -r 1 output_%06d.png

Since I’m working in octave quite often (a tool that aims to be matlab compatible), I decided to use octave for graphing. At first, I checked the same square in each and every image to get the intensity. The three lamps turned out to differ a bit in how fast they turned on, so I changed strategy.

The final strategy was to take the mean of almost all pixels in each video frame. The only pixels I excluded was those with an intensity exceeding the gray level value 200 in the final (brightest) frame, and pixels far to the right of the lamp.

Source code:

% 
% Octave script used for generating a visualization of slow turn-on time
% of long life energy saving bulbs
%
% Copyright (c) 2014-2015 Simon Gustafsson (www.optisimon.com)
%
%
% Notes about the work flow needed to generate the video:
%
% 1) Extract an image every second from a video stream:
%    ffmpeg -i /media/slow_hall_lamp.mp4 -r 1 output_%06d.png
%
% 2) Run this script. Make sure in_folder, out_folder, and plot_folder
%    points to existing folders, and that the in_folder isn't empty.
%
% 3) ffmpeg didn't want to read the images unless they was numbered from zero
%    (but this scripts first image was 17, so i'm just reusing that image)
%    for var in `seq 0 16` ; do cp combined_000017.png combined_`printf %06g $var`.png ; done
%
% 4) converting the movie into something that windows media player / vlc / virtualdub can use
%    avconv -r 24 -f image2 -i /home/simon/combined/combined_%06d.png -vcodec rawvideo -r 24 -pix_fmt bgr24 output.avi
%
% 5) Could not use the video in the video editor directly (Movie Studio Platinum 12.0),
%    so had to run it once through VirtualDub (still raw output, but fast reprocessing...)
%

page_screen_output(0);

in_folder = "slow_hall_lamp"
out_folder = "combined"
plot_folder = "plot"

% Needed to revert to using gnuplot for graphs on my ubuntu 14.04
% installation. (seems like newer versions of octave are using an opengl
% backend for plotting graphs, which refused to print the plots with my
% requested dimensions)
graphics_toolkit("gnuplot")

function plot_fname = plot_and_save(n, vals, plot_folder)
  plot([0:length(vals)-1]./60, vals)

  % Axis limits are magic values, found by plotting all the
  % vals when every frame had been processed.
  axis([0 20 0 50 0 1])

  xlabel("minutes");
  ylabel("Intensity");

  minutes = floor(n / 60);
  seconds = n - 60*minutes;
  title(sprintf("%02d:%02d [mm:ss]", minutes, seconds))
  
  plot_fname = sprintf("%s/graph_%06d.png", plot_folder, n);
  
  % Save the plot on the file system.
  %
  % Height 720 works in ubuntu 14.04 with octave 3.8.1 and gnuplot 4.6.4,
  % but I had to use heght 721 when running stock versions of tools in
  % ubuntu 12.04, so keeping a height of 721 here as a workaround for others.
  print(plot_fname, "-S522,721");
end


% Calculate a mask, so regions close to saturation won't influence 
% measured intensities
X=imread("slow_hall_lamp/output_001214.png");
X2=mean(X,3);
include_mask = (X2 <= 200);
include_mask(:,743:end) = 0; % Also mask away right side of image

figure(1);
imshow(include_mask);
title("include mask");


figure(2);
files=glob([ in_folder "/output_*.png"]);
vals = [];

for n=17:length(files) % from 17 to disregard the very first frames
  filename=files{n}

  % Read webcam image
  X=imread(filename);
  
  % Convert to gray scale, not accounting for perceived brightness
  X2=mean(X,3);

  % Get mean intensity of current frame (excluding masked regions)
  masked_img = include_mask .* X2;
  v = mean(masked_img(:))

  % Keep track of all intensities so far
  vals = [vals; v];
  
  % Graph all current intensity values, and save the plot as an image.
  % Magic number -17 since I wanted to skip the first 17 frames
  % (and we don't want seconds to start at 17)
  plot_fname = plot_and_save(n-17, vals, plot_folder);

  % Read back the plotted graph
  X_plot = imread(plot_fname);
  if size(X_plot,3) == 1
    % Oh, image library tries to be smart, won't waste bits for color if
    % a color image happens to only have black and white pixels
    X_plot(:,:,1) = X_plot;
    X_plot(:,:,2) = X_plot(:,:,1);
    X_plot(:,:,3) = X_plot(:,:,1);
  end

  % Combine the webcam image and the graph of current intensity values
  X_combined = X;
  % Doing strange thing since plotted image didn't want to be
  % 720 pixels in height...
  X_combined(:,end-size(X_plot, 2)+1:end,:) = X_plot(1:720,:,:);
 
  % Save the combined image containing the webcam image and the plot.
  combined_fname = sprintf("%s/combined_%06d.png", out_folder, n);
  imwrite(X_combined, combined_fname);
end

Graph

For reference, the last intensity vs time graph is included here as well Plot of intensity vs time