Gaslamp 1.0.4.1 Update

Gaslamp has been updated with a few minor fixes. I’ve switched to a different Geolocation provider so you can continue to acquire location and sunrise/sunset times automatically. Also fixed the “Configure” link in the menu.

In addition to this update, I’ve made some graphical changes. The new logo is here finally here as well as a new web browser icon.

Prizm 1.0 now available!

Prizm is a fully functional .NET library for working with color spaces with numerous features.

This is the first update I’ve posted in quite some time. There are several new articles I have had scheduled but due to their time-consuming nature they were put on the back burner so I could focus on my open source efforts. If you have not visited my Github profile, that is where I tend to focus most of my attention these days. I do however plan to update this site more in the next couple months including a new logo I’ve designed as well as the afforementioned new articles.

Secure Synchronization using SSH

Allows you to synchronize a directory over a secure shell connection. It also has functionality to allow you to run commands remotely, all without leaving the comfort of your local machine.

Installation

    1. Ensure Python 3 and pip are installed.
    2. Install with the following command
    pip install -r requirements.txt
    3. Run with python secure-sync.py

Usage

See secure-sync.py --help for help with parameters.

Running Commands Remotely

You can run arbitrary commands on the remote machine by populating your local directory (created by secure-sync) with a .command file. secure-sync will recognize this file and will cd to the equivalent directory on the remote machine and execute the commands in this file line by line, piping their output to your local machine.

Download

secure-sync.zip 11/04/2017 7 Kb

Dropbox Folder Restoration using Python

Restores any dropbox folder to a previous state. If a file did not exist at the specified time, it will be deleted. Handles unicode parameter input and processing of subfolders.

Example

To restore the folder /photos/tokyo to the way it was on March 7th, 2016:
python dropbox-restore.py /photos/tokyo 2016-03-07

Note: the path /photos/tokyo should be relative to your Dropbox folder; it should not include the path to the Dropbox folder on your hard drive. You will be prompted to confirm access to your Dropbox account through your web browser.

Installation

    1. Ensure Python 3.4 or higher and pip are installed.
    2. Install Dropbox Python API with the following command
    pip install dropbox
    3. Download dropbox-restore.py

Additional Notes

If you wish to modify the source code, you must obtain your own API keys from Dropbox and insert them into the APP_KEY and APP_SECRET fields.

Specifying a time is not officially supported because the time zone is ignored currently. However, it seems like Dropbox always uses UTC, so you can try specifying UTC times at your own risk by specifying the date and time in the format YYYY-MM-DD-HH-MM-SS on the command line. Be warned that Dropbox’s documentation does not guarantee that they will always use UTC, so this can break at any time.

Download

dropbox-restore.py 03/07/2016 5.5 Kb

Recursive Folder Synchronization using VBScript

This is a script written years ago that I still find useful; I had a need to apply updates to many local machines that had no network access from an external hard drive. It synchronizes the contents (files and subfolders) of two folders. Each folder is traversed recursively and any missing subfolders and files are copied both ways. If corresponding folders contain files with matching file names but with different timestamps, the file with the oldest timestamp will be overwritten.

Option Explicit

ForceScriptEngine("cscript")

Dim wshArgs
Set wshArgs = Wscript.Arguments
If WshArgs.Count = 2 Then
  Call SyncFolders(WshArgs.Item(0), WshArgs.Item(1))
  ' Also run once in reverse to catch mismatching subfolder count:
  Call SyncFolders(WshArgs.Item(1), WshArgs.Item(0))
Else
  Wscript.Echo("Wrong number of arguments. Syntax: SyncFolders Folder1 Folder2")
  Wscript.Sleep(3000) ' To allow Function syntax popup message to be seen.
End If

Sub SyncFolders(strFolder1, strFolder2)
  Dim objFileSys
  Dim objFolder1
  Dim objFolder2
  Dim objFile1
  Dim objFile2
  Dim objSubFolder
  Dim arrFolders
  Dim i
  Set objFileSys = CreateObject("Scripting.FileSystemObject")
  arrFolders = Array(strFolder1, strFolder2)
  For i = 0 To 1 ' Make sure that missing folders are created first:
    If objFileSys.FolderExists(arrFolders(i)) = False Then
      wscript.echo("Creating folder " & arrFolders(i))
      objFileSys.CreateFolder(arrFolders(i))
    End If
  Next
  Set objFolder1 = objFileSys.GetFolder(strFolder1)
  Set objFolder2 = objFileSys.GetFolder(strFolder2)
  For i = 0 To 1
    If i = 1 Then ' Reverse direction of file compare in second run
      Set objFolder1 = objFileSys.GetFolder(strFolder2)
      Set objFolder2 = objFileSys.GetFolder(strFolder1)
    End If
    For Each objFile1 in objFolder1.files
      If Not objFileSys.FileExists(objFolder2 & "\" & objFile1.name) Then
        Wscript.Echo("Copying " & objFolder1 & "\" & objFile1.name & _
          " to " & objFolder2 & "\" & objFile1.name)
        objFileSys.CopyFile objFolder1 & "\" & objFile1.name, _
          objFolder2 & "\" & objFile1.name
      Else
        Set objFile2 = objFileSys.GetFile(objFolder2 & "\" & objFile1.name)
        If objFile1.DateLastModified > objFile2.DateLastModified Then
          Wscript.Echo("Overwriting " & objFolder2 & "\" & objFile1.name & _
            " with " & objFolder1 & "\" & objFile1.name)
          objFileSys.CopyFile objFolder1 & "\" & objFile1.name, _
            objFolder2 & "\" & objFile1.name    
        End If
      End If
    Next
  Next
  For Each objSubFolder in objFolder1.subFolders
    Call SyncFolders(strFolder1 & "\" & objSubFolder.name, strFolder2 & _
      "\" & objSubFolder.name)
  Next
  Set objFileSys = Nothing
End Sub

Sub ForceScriptEngine(strScriptEng)
  ' Forces this script to be run under the desired scripting host.
  ' Valid arguments are "wscript" or "cscript".
  ' The command line arguments are passed on to the new call.
  Dim arrArgs
  Dim strArgs
  For Each arrArgs In WScript.Arguments
    strArgs = strArgs & " " & Chr(34) & arrArgs & Chr(34)
  Next
  If Lcase(Right(Wscript.FullName, 12)) = "\wscript.exe" Then
    If Instr(1, Wscript.FullName, strScriptEng, 1) = 0 Then
      CreateObject("Wscript.Shell").Run "cscript.exe //Nologo " & _
        Chr(34) & Wscript.ScriptFullName & Chr(34) & strArgs
      Wscript.Quit
    End If
  Else
    If Instr(1, Wscript.FullName, strScriptEng, 1) = 0 Then
      CreateObject("Wscript.Shell").Run "wscript.exe " & Chr(34) & _
        Wscript.ScriptFullName & Chr(34) & strArgs
      Wscript.Quit
    End If
  End If
End Sub

When only one-way sync is needed (only folder 2 is modified), use this version:

Option Explicit

ForceScriptEngine("cscript")

Dim wshArgs
Set wshArgs = Wscript.Arguments
If WshArgs.Count = 2 Then
  Call SyncFolders(WshArgs.Item(0), WshArgs.Item(1))
Else
  Wscript.Echo("Wrong number of arguments. Syntax: SyncFolders Folder1 Folder2")
  Wscript.Sleep(3000) ' To allow Function syntax popup message to be seen.
End If

Sub SyncFolders(strFolder1, strFolder2)
  ' Note: This version only copies from folder1 to folder2
  Dim objFileSys
  Dim objFolder1
  Dim objFolder2
  Dim objFile1
  Dim objFile2
  Dim objSubFolder
  Dim arrFolders
  Dim i
  arrFolders = Array(strFolder1, strFolder2)
  Set objFileSys = CreateObject("Scripting.FileSystemObject")
  For i = 0 To 1 ' Make sure that missing folders are created first:
    If objFileSys.FolderExists(arrFolders(i)) = False Then
      wscript.echo("Creating folder " & arrFolders(i))
      objFileSys.CreateFolder(arrFolders(i))
    End If
  Next
  Set objFolder1 = objFileSys.GetFolder(strFolder1)
  Set objFolder2 = objFileSys.GetFolder(strFolder2)
  For Each objFile1 in objFolder1.files
    If Not objFileSys.FileExists(objFolder2 & "\" & objFile1.name) Then
      Wscript.Echo("Copying " & objFolder1 & "\" & objFile1.name & _
        " to " & objFolder2 & "\" & objFile1.name)
      objFileSys.CopyFile objFolder1 & "\" & objFile1.name, _
        objFolder2 & "\" & objFile1.name
    Else
      Set objFile2 = objFileSys.GetFile(objFolder2 & "\" & objFile1.name)
      If objFile1.DateLastModified > objFile2.DateLastModified Then
        Wscript.Echo("Overwriting " & objFolder2 & "\" & objFile1.name & _
          " with " & objFolder1 & "\" & objFile1.name)
        objFileSys.CopyFile objFolder1 & "\" & objFile1.name, _
          objFolder2 & "\" & objFile1.name    
      End If
    End If
  Next
  For Each objSubFolder in objFolder1.subFolders
    Call SyncFolders(strFolder1 & "\" & objSubFolder.name, strFolder2 & _
      "\" & objSubFolder.name)
  Next
  Set objFileSys = Nothing
End Sub

Sub ForceScriptEngine(strScriptEng)
  ' Forces this script to be run under the desired scripting host.
  ' Valid arguments are "wscript" or "cscript".
  ' The command line arguments are passed on to the new call.
  Dim arrArgs
  Dim strArgs
  For Each arrArgs In WScript.Arguments
    strArgs = strArgs & " " & Chr(34) & arrArgs & Chr(34)
  Next
  If Lcase(Right(Wscript.FullName, 12)) = "\wscript.exe" Then
    If Instr(1, Wscript.FullName, strScriptEng, 1) = 0 Then
      CreateObject("Wscript.Shell").Run "cscript.exe //Nologo " & _
        Chr(34) & Wscript.ScriptFullName & Chr(34) & strArgs
      Wscript.Quit
    End If
  Else
    If Instr(1, Wscript.FullName, strScriptEng, 1) = 0 Then
      CreateObject("Wscript.Shell").Run "wscript.exe " & Chr(34) & _
        Wscript.ScriptFullName & Chr(34) & strArgs
      Wscript.Quit
    End If
  End If
End Sub

ClockworkPDF Beta Release

Finally it is here. I apologize for the delay as I’ve been working on a couple other projects concurrently. This is the first public and testable version, don’t except it to be stable and production ready. I’ve tested it much as I can but if you encounter problems with certain PDF versions, please let me know. Any feedback, ideas for new features or suggestions are gladly welcome.

Oil Painting Filter in C#

Converting an image to make it look like an oil painting is not only a nice effect, but is also an easy to implement algorithm. This article demonstrates how to create an oil painting effect from an image. The oil painting filter consists of two main components: color gradients and pixel color intensities. Result images express a lesser degree of detail and tend to appear to have smaller color ranges.

Implementation

The following steps are required for the algorithm:

  1. Iterate each pixel – Every pixel forming part of the source image should be iterated. When iterating a pixel determine the neighboring pixel values based on the specified filter size (or radius).
  2. Calculate color intensityDetermine the Color Intensity of each pixel being iterated and that of the neighboring pixels. The neighboring pixels included should extend to a range determined by the Filter Size specified. The calculated value should be reduced in order to match a value ranging from zero to the number of Intensity Levels specified.
  3. Determine maximum neighborhood color intensity – When calculating the color intensities of a pixel neighborhood determine the maximum intensity value. In addition, record the occurrence of each intensity level and sum each of the Red, Green and Blue pixel color component values equating to the same intensity level.
  4. Assign the result pixel – The value assigned to the corresponding pixel in the resulting image equates to the pixel color sum total, where those pixels expressed the same intensity level. The sum total should be averaged by dividing the color sum total by the intensity level occurrence

When calculating color intensity reduced to fit the number of levels specified the formula can be expressed as follows:

colorintensityalgorithm.png

The variables in the above formula can be described as follows:

  • i – Intensity: the calculated intensity value.
  • R – Red: The value of the pixel’s red color component.
  • G – Green:
  • B – Blue:
  • l – Number of intensity levels: the maximum number of intensity levels specified.

Result Image: Filter 15, Levels 30

Oil Painting - Filter 15, Levels 30

The sample source code defines OilPaintFilter method, an extension method targeting the Bitmap class. This method determines the maximum color intensity from a pixel’s neighbors.

public static Bitmap OilPaintFilter(this Bitmap sourceBitmap, int levels, int filterSize)
        {
            Bitmap image;
            ArgbColor[] originalData;
            ArgbColor[] resultData;
            Size size;

            image = sourceBitmap;
            size = image.Size;

            originalData = image.GetPixelsFrom32BitArgbImage();
            resultData = new ArgbColor[size.Width * size.Height];

            int[] intensityBin = new int[levels];
            int[] blueBin = new int[levels];
            int[] greenBin = new int[levels];
            int[] redBin = new int[levels];

            levels = levels - 1;

            int filterOffset = (filterSize - 1) / 2;
            int byteOffset = 0;
            int calcOffset = 0;
            int currentIntensity = 0;
            int maxIntensity = 0;
            int maxIndex = 0;

            double blue = 0;
            double green = 0;
            double red = 0;

            for (int offsetY = filterOffset; offsetY < size.Height - filterOffset; offsetY++)
            {
                for (int offsetX = filterOffset; offsetX < size.Width - filterOffset; offsetX++)
                {
                    ArgbColor result;

                    blue = green = red = 0;

                    currentIntensity = maxIntensity = maxIndex = 0;

                    intensityBin = new int[levels + 1];
                    blueBin = new int[levels + 1];
                    greenBin = new int[levels + 1];
                    redBin = new int[levels + 1];

                    byteOffset = offsetY * size.Width + offsetX;

                    for (int filterY = -filterOffset; filterY <= filterOffset; filterY++)
                    {
                        for (int filterX = -filterOffset; filterX <= filterOffset; filterX++)
                        {
                            calcOffset = byteOffset + (filterX) + (filterY * size.Width);

                            currentIntensity = (int)Math.Round(((double)(originalData[calcOffset].R +
                                                originalData[calcOffset].G + originalData[calcOffset].B) / 3.0 * (levels)) / 255.0);

                            intensityBin[currentIntensity] += 1;
                            blueBin[currentIntensity] += originalData[calcOffset].B;
                            greenBin[currentIntensity] += originalData[calcOffset].G;
                            redBin[currentIntensity] += originalData[calcOffset].R;

                            if (intensityBin[currentIntensity] > maxIntensity)
                            {
                                maxIntensity = intensityBin[currentIntensity];
                                maxIndex = currentIntensity;
                            }
                        }
                    }

                    blue = blueBin[maxIndex] / maxIntensity;
                    green = greenBin[maxIndex] / maxIntensity;
                    red = redBin[maxIndex] / maxIntensity;

                    result.A = 255;
                    result.R = ClipByte(red);
                    result.G = ClipByte(green);
                    result.B = ClipByte(blue);

                    resultData[byteOffset] = result;
                }
            }

            return resultData.ToBitmap(size); ;
        }

Downloads

OilPaintingFilter.zip 13 Nov 2014 137 KB

iTunes 11 Style Scroller

Spent a couple of hours seeing if I could recreate the album scroller found in the store view of the latest version of iTunes. Uses CSS3 transforms for animation and some jQuery to handle interaction. Definitely needs tidying up, and probably only works in WebKit and latest browsers for now.

Click Me

Kuwahara Filter in C#

The Kuwahara filter is probably one of those things that you haven’t heard of much, if ever. It’s a noise reduction/blurring technique for images that preserves edges, similar to a median filter. It acts like a box blur, except we take that box around the center pixel and divide that into four smaller boxes (with some overlap since the number of items in the box is usually odd along the x and y axis). These smaller boxes are calculated in the same fashion as the box blur for the average value. While we’re doing that, we’re also performing another step: Finding the variance for the box. We want to know which box has the least amount of variance. The variance can be calculated in a number of ways but the easiest is simply finding the minimum and maximum values (for r, g and b) for a box and subtracting the min from the max.

public static Bitmap KuwaharaBlur(Bitmap Image, int Size)
{
    System.Drawing.Bitmap TempBitmap = Image;
    System.Drawing.Bitmap NewBitmap = new System.Drawing.Bitmap(TempBitmap.Width, TempBitmap.Height);
    System.Drawing.Graphics NewGraphics = System.Drawing.Graphics.FromImage(NewBitmap);
    NewGraphics.DrawImage(TempBitmap, new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), System.Drawing.GraphicsUnit.Pixel);
    NewGraphics.Dispose();
    Random TempRandom = new Random();
    int[] ApetureMinX = { -(Size / 2), 0, -(Size / 2), 0 };
    int[] ApetureMaxX = { 0, (Size / 2), 0, (Size / 2) };
    int[] ApetureMinY = { -(Size / 2), -(Size / 2), 0, 0 };
    int[] ApetureMaxY = { 0, 0, (Size / 2), (Size / 2) };
    for (int x = 0; x < NewBitmap.Width; ++x)
    {
        for (int y = 0; y < NewBitmap.Height; ++y)
        {
            int[] RValues = { 0, 0, 0, 0 };
            int[] GValues = { 0, 0, 0, 0 };
            int[] BValues = { 0, 0, 0, 0 };
            int[] NumPixels = { 0, 0, 0, 0 };
            int[] MaxRValue = { 0, 0, 0, 0 };
            int[] MaxGValue = { 0, 0, 0, 0 };
            int[] MaxBValue = { 0, 0, 0, 0 };
            int[] MinRValue = { 255, 255, 255, 255 };
            int[] MinGValue = { 255, 255, 255, 255 };
            int[] MinBValue = { 255, 255, 255, 255 };
            for (int i = 0; i < 4; ++i)
            {
                for (int x2 = ApetureMinX[i]; x2 < ApetureMaxX[i]; ++x2)
                {
                    int TempX = x + x2;
                    if (TempX >= 0 && TempX < NewBitmap.Width)
                    {
                        for (int y2 = ApetureMinY[i]; y2 < ApetureMaxY[i]; ++y2)
                        {
                            int TempY = y + y2;
                            if (TempY >= 0 && TempY < NewBitmap.Height)
                            {
                                Color TempColor = TempBitmap.GetPixel(TempX, TempY);
                                RValues[i] += TempColor.R;
                                GValues[i] += TempColor.G;
                                BValues[i] += TempColor.B;
                                if (TempColor.R > MaxRValue[i])
                                {
                                    MaxRValue[i] = TempColor.R;
                                }
                                else if (TempColor.R < MinRValue[i])
                                {
                                    MinRValue[i] = TempColor.R;
                                }
 
                                if (TempColor.G > MaxGValue[i])
                                {
                                    MaxGValue[i] = TempColor.G;
                                }
                                else if (TempColor.G < MinGValue[i])
                                {
                                    MinGValue[i] = TempColor.G;
                                }
 
                                if (TempColor.B > MaxBValue[i])
                                {
                                    MaxBValue[i] = TempColor.B;
                                }
                                else if (TempColor.B < MinBValue[i])
                                {
                                    MinBValue[i] = TempColor.B;
                                }
                                ++NumPixels[i];
                            }
                        }
                    }
                }
            }
            int j = 0;
            int MinDifference = 10000;
            for (int i = 0; i < 4; ++i)
            {
                int CurrentDifference = (MaxRValue[i] - MinRValue[i]) + (MaxGValue[i] - MinGValue[i]) + (MaxBValue[i] - MinBValue[i]);
                if (CurrentDifference < MinDifference && NumPixels[i] > 0)
                {
                    j = i;
                    MinDifference = CurrentDifference;
                }
            }
 
            Color MeanPixel = Color.FromArgb(RValues[j] / NumPixels[j],
                GValues[j] / NumPixels[j],
                BValues[j] / NumPixels[j]);
            NewBitmap.SetPixel(x, y, MeanPixel);
        }
    }
    return NewBitmap;
}

The code above takes in an image as well as the size that you want the aperture to be. In turn it gives you a smoothed image. This filter technique is quite different from other blur filters as it produces a lot more stylized look. Hopefully this helps you out in some way.

Craigslist Monitor using Ruby

Overview

To add to my ever expanding knowledge base of languages, I decided to spend the last day and a half teaching myself Ruby and this was the result. A script to watch Craigslist for relevant posts so you don’t have to. My first ever Ruby program.

Watching Craigslist can be a time-consuming task. The script automates searching for housing, sales, services, jobs, and gigs, and emails you when it find something that matches your search criteria. It also works across multiple cities, since it’s controlled by the RSS feeds you configure it with.

Approach

Instead of sending Craigslist a server-side query, I decided to just get the RSS feed and filter the results on the client-side. There are two advantages of this — server changes don’t break the code and you can watch other feeds in a similar way (e.g. eBay).

Configuration

Edit the monitor.yml file to your liking. It should look something like this:

---
feeds:
  - http://sfbay.craigslist.org/wri/index.rss
  - http://sandiego.craigslist.org/wri/index.rss
include: software, developer, computer, programmer
exclude: freelance, internship, supplement, contract
email_to: someone@gmail.com
email_from:
  address: smtp.gmail.com
  port: 587
  user_name: 'a_username'
  password: 'a_password'

Running

clockwork monitor.rb

That’s it. if you want to log the output:

clockwork monitor.rb > monitor.log

Press CTRL+C to exit

Downloads

CLMonitor.zip 19 Sep 2014 3 KB