About Pic2HTML

Pic2HTML is a program to transform pictures from binary format (*.gif, *.jpg) into pure html-text format. In this way (small) images can be included directly with the text of the html page. See the following examples (click left, to open in popup window): Images can be transformed with various options: black-white/colored, high quality/low quality, transition smoothing and others. For halftoning of images are several algorithms implemented, which are presented in the following section a bit more in detail. For better comparision with other algorithms, the well-known Lena image is used as example image.

Digital Halftoning of the Lena image


a) original lena image

b) halftoning by ordered dither with a 2x2 pattern

c) halftoning along a fractal path

d) halftoning along a fractal path with corrected greyvalues

e) halftoning by ordered dither with transition smoothing
larger image as pure HTML (374kB)

f) halftoning by ordered dither with pattern orientation by hilbert curve and transition smoothing
larger image as pure HTML (469kB)

original grey scale ramp image

halftoning by algorithm used in image b)

halftoning by algorithm used in image c)

halftoning by algorithm used in image d)

halftoning by algorithm used in image e)

halftoning by algorithm used in image f)

VB code for halftoning from my program Pic2HTML

  Dim ccStrP, ccStr0, ccStr1
  Dim ccStr(4)
  ccStrP = Array("II", "I.", "I ", ". ", "  ")
  ccStr0 = Array("II", "I'", ".'", ". ", "  ")
  ccStr1 = Array("II", "I.", "'.", "' ", "  ")
  ccStr(0) = Array("II", "I.", "I ", "' ", "  ")
  ccStr(1) = Array("II", "'I", "''", "' ", "  ")
  ccStr(2) = Array("II", "'I", " I", " .", "  ")
  ccStr(3) = Array("II", "I.", "..", " .", "  ")

'...some lines have been erased here
 
            cc = (2 * rr + 3 * gg + bb) 'used weights for red green and blue
            Select Case BWEncoding
              Case 0: '-----------SEE IMAGE b)------------------
                cc = cc \ 308 'cc=0, 1, 2, 3, 4
                iiHTML = iiHTML & ccStrP(cc)
              Case 1: '-----------SEE IMAGE e)------------------
                cc = cc + ((xx + yy) Mod 2) * 192 - 96 'smoothing
                If cc < 0 Then cc = 0
                cc = cc \ 308 'cc=0, 1, 2, 3, 4
                If cc > 4 Then cc = 4
                If (xx + yy) Mod 2 = 0 Then
                  iiHTML = iiHTML & ccStr0(cc)
                Else
                  iiHTML = iiHTML & ccStr1(cc)
              End If
              Case 2: '-----------SEE IMAGE f)------------------
                cc = cc + ((xx + yy) Mod 2) * 192 - 96 'smoothing
                If cc < 0 Then cc = 0
                cc = cc \ 308 'c=0, 1, 2, 3, 4
                If cc > 4 Then cc = 4
                hh_Shape = GetHilbertShape(xx, yy) 
                iiHTML = iiHTML & ccStr(hh_Shape)(cc)
            End Select

'The function GetHilbertshape(i, j) returns 0, 1, 2 or 3, which represents the
'orientation of a hilbert curve of a size 2^(2*n) at pixel position (i, j)
'see code below:
------------------------------------------------------------------
Function GetHilbertShape(aii As Integer, ajj As Integer) As Integer
  Dim ss, ii, jj
  ii = 4 * aii + ajj 'coordinate trafo to get rid of artifacts
  jj = 4 * ajj + aii 'try out other combinations too!
  ss = 1
  While ((ii >= ss) Or (jj >= ss))
    ss = ss * 4
  Wend
  GetHilbertShape = GetHilbertShapeEven(ii, jj, ss, 0)
  'GetHilbertShape = Int((4 * Rnd)) 'check out a random distribution
End Function

------------------------------------------------------------------
Function GetHilbertShapeEven(ByVal axx As Integer, ByVal ayy As Integer, ByVal asize As Integer, ByVal arot As Integer) As Integer
  If asize = 1 Then
    GetHilbertShapeEven = arot Mod 4
    Exit Function
  End If
  Dim xx, yy, size2, rot
  xx = axx: yy = ayy: size2 = asize \ 2: rot = arot
  If (yy < size2) Then
   If (xx < size2) Then
      GetHilbertShapeEven = GetHilbertShapeOdd(xx, yy, size2, rot)
    Else
      GetHilbertShapeEven = GetHilbertShapeOdd(2 * size2 - 1 - xx, size2 - 1 - yy, size2, rot + 2)
    End If
  Else
    If (xx < size2) Then
      GetHilbertShapeEven = GetHilbertShapeOdd(yy - size2, size2 - 1 - xx, size2, rot + 3)
    Else
      GetHilbertShapeEven = GetHilbertShapeOdd(yy - size2, 2 * size2 - 1 - xx, size2, rot + 3)
    End If
  End If
End Function
----------------------------------------------------------------------
Function GetHilbertShapeOdd(ByVal axx As Integer, ByVal ayy As Integer, ByVal asize As Integer, ByVal arot As Integer) As Integer
  Dim xx, yy, size2, rot
  xx = axx: yy = ayy: size2 = asize / 2: rot = arot
  If (yy < size2) Then
    If (xx < size2) Then
      GetHilbertShapeOdd = GetHilbertShapeEven(xx, yy, size2, rot)
    Else
      GetHilbertShapeOdd = GetHilbertShapeEven(size2 - 1 - yy, xx - size2, size2, rot + 1)
    End If
  Else
    If (xx < size2) Then
      GetHilbertShapeOdd = GetHilbertShapeEven(size2 - 1 - xx, 2 * size2 - 1 - yy, size2, rot + 2)
    Else
      GetHilbertShapeOdd = GetHilbertShapeEven(2 * size2 - 1 - yy, xx - size2, size2, rot + 1)
    End If
  End If
End Function

The Images c) and d) have been generated by walking on this fractal spacefilling curve and applying the following halftoning algorithm:
Sub DitherFract(ByVal i As Integer, ByVal j As Integer)
  Dim rr As Integer
  Dim gg As Integer
  Dim bb As Integer
  Dim cc As Long
'  Dim cc_128 As Long
'  Dim cc_min As Long
'  cc_128 = 160
'  cc_min = 10
  If ((i <= imax) And (j <= jmax)) Then
    cc = Pic.Point(i, j)
    Long2RGB cc, rr, gg, bb
    cc = (2 * rr + 3 * gg + bb) \ 6
'    If cc <= cc_128 Then
'      cc = cc * (110 - cc_min) \ cc_128 + cc_min
'    Else
'      cc = (cc - cc_128) * (255 - 146) \ (255 - cc_128) + 146
'    End If
    cc = cc - 128
    DitherError = DitherError + CInt(cc) * 32 \ 31
    If DitherErro <= 0 Then
      Pic.PSet (i, j), RGB(0, 0, 0)
      DitherError = DitherError + 128
    Else
      Pic.PSet (i, j), RGB(255, 255, 255)
      DitherError = DitherError - (255 - 128)
    End If
  End If
  DitherError = DitherError * 31 \ 32
End Sub

Halftoning with spacefilling curves - color error vs. spatial error

The Algorithm above uses a factor of 31/32 for the propagation of the dither error, while walking on a fractal curve. That means, for example, if at the first pixel position the grey value is 127, then the halftone value is 0 for this pixel and the dither error is 127. Assume the next n pixel on the fractal path have a grey value of 0 (and will be halftoned to 0), then a pixel n+1 must have a grey value of at least 128-127*(31/32)^n, to be halftoned to 255. To use a factor of 1 instead of (31/32) would minimize the color error to 0, because no dither error would be lost, however the spatial error would be maximal. That means the color is often assigned to the wrong position, resulting in a 'blurred' picture. At the other hand, when using a factor of 0, then spatial error would be 0, however the color error would be maximal.

color error minimal f=1

intermediate f=3/4

spatial error minimal f=0
compare with alg. e) (44kB) and alg. f) (56kB) (both as pure HTML pictures)
The 'optimal' factor f is not a constant, it depends on the image. In general for a larger image the factor should be closer to 1, in order to reduce the color error and also the 'relative' spatial error. (A 200x200 image with an average spatial error of 3 pixel has a smaller 'relative' spatial error than a 100x100 image image with an average spatial error of 2 pixel, although the 'absolute' spatial error of the 200x200 image is higher). Further it is important to use a space filling curve which is 'dense', that means the after walking n steps from a pixel, the average distance of the new pixel from the original pixel should be as small as possible. See the comparision of the following three spacefilling curves:
avg. distance
when walking
1 Pixel2 Pixel3 Pixel4 Pixel 
1: 1/1
avg: 1
2: 1/1
avg: 2
3: 1/1
avg: 3
4: 1/1
avg: 4
LOSER
1: 1/1
avg: 1
sqrt(2): 2/3
2: 1/3
avg: 1.61
1: 6/27
sqrt(5): 20/27
3: 1/27
avg: 1.99
sqrt(2): 5/27
2: 12/27
sqrt(8): 7/27
sqrt(10): 3/27
avg: 2.23
second
winner
1: 1/1
avg: 1
sqrt(2): 4/5
2: 1/5
avg: 1.53
1: 19/60
sqrt(5): 40/60
3: 1/60
avg: 1.86
sqrt(2): 1/5
2: 3/5
sqrt(10): 1/5
avg: 2.12
WINNER


There is also a method for generating a spacefilling curve of arbitrary size.

Lutz Tautenhahn, 2003

Update 2024:
The old Window exe program from 2003 is here: Pic2HTML.zip. The html files generated by the program must be updated by hand (change 'Arial' to 'Courier').
A new web version of the old program is here: pic2html.html. Images need to be loaded from the web, the remote host must be configured to allow CORS.