Seite 1 von 1

ReduceImageColors()

Verfasst: 12.08.2005 10:57
von Froggerprogger
Ahoi.
Folgender Code reduziert die Anzahl Farben eines Bildes auf 2-16 Farben.
Ihm liegt allerdings kein bekannter+bewährter Algorithmus zugrunde, sondern ist lediglich auf meinen eigenen Mist gewachsen, aber immerhin tut er einigermaßen das, was er soll.

Ahoi,
Froggerprogger

Code: Alles auswählen

;- ReduceImageColors() v1.0
;- Reduces the number of colors of an image to 2-16 colors
;- and finds adequate colors for that.
;- Returns a new created copy containing the reduced image.
;- The algorithm will not be the best out there (was just
;- based on some simple thoughts), but it works!
;- 
;- 12.08.05 by Froggerprogger

Structure RGBCountElement
  red.l
  green.l
  blue.l
  count.l
EndStructure 

Dim InputImage.l(0,0)
Dim ImageColors4096.RGBCountElement(4095)
Dim OutputColors.RGBCountElement(0)

Procedure ReduceImageColors(imageNumber.l, numColors.l) ; Reduces the number of colors to 1 - 16 colors. Returns the new image.
  If numColors < 2 Or numColors > 16 Or IsImage(imageNumber) = 0
    ProcedureReturn 0
  EndIf
  
  Protected width.l, height.l, color.l, red.l, green.l, blue.l, index.l
  Protected currentMaximumPos.l
  Protected i.l, j.l, k.l, l.l, m.l
  
  UseImage(imageNumber)
  width = ImageWidth()
  height = ImageHeight()
  
  Dim InputImage.l(width-1, height-1)
  StartDrawing(ImageOutput())
    For i=0 To width-1
      For j=0 To height-1
        color = Point(i,j)
        ; 'copy' the image to an array
        InputImage(i,j) = color
        ;count the color-hits
        red   = Int(Red(color) / 16)
        green = Int(Green(color) / 16)
        blue  = Int(Blue(color) / 16)
        index = red + green << 4 + blue << 8
        ImageColors4096(index)\count + 1
      Next
    Next
  StopDrawing()
 
  
  Dim OutputColors.RGBCountElement(numColors-1)
  For i=0 To numColors-1
    currentMaximumPos = 0
    ; get the most often value
    For j=1 To 4095
      If ImageColors4096(j)\count > ImageColors4096(currentMaximumPos)\count
        currentMaximumPos = j
      EndIf
    Next
 
    OutputColors(i)\red   = currentMaximumPos & $F
    OutputColors(i)\green = (currentMaximumPos >> 4) & $F
    OutputColors(i)\blue  = (currentMaximumPos >> 8) & $F
    
    ; remove entries that are closest to the current outputcolor
    removeNum = 1 + width * height / numColors
    removed = 0
    ignoreTheseWays = 0
    For j=0 To 15
      For k=0 To 15
        For l=0 To 15
          For m=0 To 7
            ; bitfield used for speeding up
            If ignoreTheseWays & (1 << m)
              Continue
            EndIf

            If m & %1
              red = OutputColors(i)\red + j
              If red > 15
                Continue
              EndIf
            Else
              red = OutputColors(i)\red - j
              If red < 0
                Continue
              EndIf              
            EndIf
            If m & %10
              green = OutputColors(i)\green + k
              If green > 15
                Continue
              EndIf
            Else
              green = OutputColors(i)\green - k
              If green < 0
                Continue
              EndIf              
            EndIf
            If m & %100
              blue = OutputColors(i)\blue + l
              If blue > 15
                Continue
              EndIf
            Else
              blue = OutputColors(i)\blue - l
              If blue < 0
                Continue
              EndIf              
            EndIf            

            index = red + green << 4 + blue << 8
            
            If ImageColors4096(index)\count = -1
              ignoreTheseWays = ignoreTheseWays | (1 << m)
              If ignoreTheseWays = %11111111
                Break 4
              EndIf
            EndIf
            
            removed = removed + ImageColors4096(index)\count
            ImageColors4096(index)\count = -1
            ImageColors4096(index)\red   = red
            ImageColors4096(index)\green = green
            ImageColors4096(index)\blue  = blue

            If totalremoved + removed >= width * height
              Break 5
            EndIf 
            If removed >= removeNum
              Break 4
            EndIf
          Next
        Next
      Next
    Next 
    totalremoved + removed
  Next
  
  outputimage = CreateImage(#PB_Any, width, height)
  StartDrawing(ImageOutput())
    For i=0 To width-1
      For j=0 To height-1
        color = InputImage(i,j)
        red   = Int(Red(color) / 16)
        green = Int(Green(color) / 16)
        blue  = Int(Blue(color) / 16)

        minindex = 0
        minval = Abs(red - OutputColors(0)\red) + Abs(green - OutputColors(0)\green) + Abs(blue - OutputColors(0)\blue)
        For k=1 To numColors-1
          tmp = Abs(red - OutputColors(k)\red) + Abs(green - OutputColors(k)\green) + Abs(blue - OutputColors(k)\blue)
          If tmp < minval
            minval = tmp
            minindex = k
          EndIf
        Next
        OutputColors(minindex)\count + 1
        FrontColor(OutputColors(minindex)\red * 16, OutputColors(minindex)\green * 16, OutputColors(minindex)\blue * 16)
        Plot(i, j)
      Next
    Next
    StopDrawing()
    
    ProcedureReturn outputimage
EndProcedure

;- example
inputimg = LoadImage(#PB_Any, "test.bmp")
outputimg = ReduceImageColors(inputimg, 16)
SaveImage(outputimg, "output16.bmp")
outputimg = ReduceImageColors(inputimg, 4)
SaveImage(outputimg, "output4.bmp")
MessageRequester("", "done")