You guys are going about this the wrong way. HSV is the wrong color model to be using. It's slow to convert, and you lose a lot of the color accuracy in the conversion. And if you use RGB, then you have to do 3x the work, and can get color halos.
Try this:
; Floating point image format with luminosity and two color channels:
Type IMAGE_YCC
Field Height
Field Width
Field Bank[3]
End Type
; Used by YCC_ChannelAverageMinMax function:
Global YCC_ChannelAverage#
Global YCC_ChannelMin#
Global YCC_ChannelMax#
; -----------------------------------------------------------------------------------------------------------------------------------
; This function creates a new YCC image, and allocates the memory it needs for storage.
; It returns a pointer to the new image.
;
; ChannelFlags is an optional parameter which defines which channels you want the image to contain.
; By default, all channels are created. The flags are as follows: Y=1, Cb=2, Cr=4
; -----------------------------------------------------------------------------------------------------------------------------------
Function CreateYCCImage.IMAGE_YCC(IWidth, IHeight, ChannelFlags=1+2+4)
Local ISize
Local NewImage.IMAGE_YCC
Local Channel
NewImage = New IMAGE_YCC
NewImage\Width = IWidth
NewImage\Height = IHeight
ISize = IWidth * IHeight * 4
For Channel = 0 To 2
If ChannelFlags And 2^Channel
NewImage\Bank[Channel] = CreateBank(ISize)
EndIf
Next
Return NewImage
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This function copies a YCC image, and allocates the memory the copy needs for storage.
; It returns a pointer to the new image.
;
; ChannelFlags is an optional parameter which defines which channels of the source image you want to copy.
; By default, all channels are copied. The flags are as follows: Y=1, Cb=2, Cr=4
; -----------------------------------------------------------------------------------------------------------------------------------
Function CopyYCCImage.IMAGE_YCC(ImageToCopy.IMAGE_YCC, ChannelFlags=1+2+4)
Local NewImage.IMAGE_YCC
Local ISize
NewImage = New IMAGE_YCC
NewImage\Width = ImageToCopy\Width
NewImage\Height = ImageToCopy\Height
ISize = NewImage\Width * NewImage\Height * 4
For Channel = 0 To 2
If ChannelFlags And 2^Channel
NewImage\Bank[Channel] = CreateBank(ISize)
CopyBank ImageToCopy\Bank[Channel], 0, NewImage\Bank[Channel], 0, ISize
EndIf
Next
Return NewImage
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This function copies one YCC image over another.
;
; ChannelFlags is an optional parameter which defines which channels of the source image you want to copy.
; By default, all channels are copied. The flags are as follows: Y=1, Cb=2, Cr=4
; -----------------------------------------------------------------------------------------------------------------------------------
Function CopyYCCImageTo(SrcImage.IMAGE_YCC, DestImage.IMAGE_YCC, ChannelFlags=1+2+4)
Local ISize
ISize = SrcImage\Width * SrcImage\Height * 4
For Channel = 0 To 2
If ChannelFlags And 2^Channel
CopyBank SrcImage\Bank[Channel], 0, DestImage\Bank[Channel], 0, ISize
EndIf
Next
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This function deletes a YCC image, and frees the banks it used for storage.
; -----------------------------------------------------------------------------------------------------------------------------------
Function FreeYCCImage(YCC.IMAGE_YCC)
; If the specified YCC image exists...
If YCC <> Null
FreeBank YCC\Bank[0]
FreeBank YCC\Bank[1]
FreeBank YCC\Bank[2]
Delete YCC
EndIf
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This function converts an image from RGB format to floating-point YCC format.
; The new image data is stored in a set of three buffers in a type, an a pointer to the type is returned.
;
; YCC format is Y Cb Cr, where Y is the luminosity, and Cb and Cr are color channels.
; There are several versions of YCC, I use the same one used for JPEG images.
; -----------------------------------------------------------------------------------------------------------------------------------
Function ConvertImagetoYCC.IMAGE_YCC(SrcImage)
Local DestImage.IMAGE_YCC
Local SrcBuffer
Local IWidth, IHeight
Local LoopX, LoopY
Local YOffset, Offset
Local RGB
Local R, G, B
Local Rf#, Gf#, Bf#
Local Y#, Cb#, Cr#
; Get the source image buffer.
SrcBuffer = ImageBuffer(SrcImage)
; Store the height and width of the source image.
IWidth = ImageWidth(SrcImage)
IHeight = ImageHeight(SrcImage)
; Create an image in YCC format for the destination.
DestImage = CreateYCCImage(IWidth, IHeight)
; Loop through each pixel in the image and copy it to the floating point image banks.
; (Pixels are stored as a value between 0 and 1)
LockBuffer(SrcBuffer)
For LoopY = 0 To IHeight-1
YOffset = LoopY*IWidth
For LoopX = 0 To IWidth-1
Offset = (YOffset + LoopX) Shl 2
; Get color of pixel.
RGB = ReadPixelFast(LoopX, LoopY, SrcBuffer)
R = (RGB Shr 16) And 255
G = (RGB Shr 8 ) And 255
B = (RGB ) And 255
; Adjust pixel color to fit the range 0..1.
Rf# = R / 255.0
Gf# = G / 255.0
Bf# = B / 255.0
; Convert pixel color to YCbCr color space.
; RGB -> YCbCr (CCIR 601-1 JPEG)
Y# = 0.29900*Rf# + 0.58700*Gf# + 0.11400*Bf#
Cb# = -0.16874*Rf# - 0.33126*Gf# + 0.50000*Bf#
Cr# = 0.50000*Rf# - 0.41869*Gf# - 0.08131*Bf#
Cb# = Cb# + 0.5
Cr# = Cr# + 0.5
; Store color in bank.
PokeFloat DestImage\Bank[0], Offset, Y#
PokeFloat DestImage\Bank[1], Offset, Cb#
PokeFloat DestImage\Bank[2], Offset, Cr#
Next
Next
UnlockBuffer(SrcBuffer)
; Return the pointer to the new image.
Return DestImage
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This function converts imagedata in floating-point YCC format back into a standard RGB image.
; The specified image is the destination, and is overwritten.
; -----------------------------------------------------------------------------------------------------------------------------------
Function ConvertYCCtoImage(SrcImage.IMAGE_YCC, ChannelFlags=1+2+4)
Local DestBuffer
Local LoopX, LoopY
Local Offset, YOffset
Local Rf#, Gf#, Bf#
Local Y#, Cb#, Cr#
Local R, G, B
Local RGB
Local IWidth, IHeight
; Get the width and height of the source image.
IWidth = SrcImage\Width
IHeight = SrcImage\Height
; Create an image for the destination.
DestImage = CreateImage(IWidth, IHeight, 1, 1)
; Convert the source image to RGB and store it in the destination.
; YCbCr -> RGB (CCIR 601-1 JPEG)
DestBuffer = ImageBuffer(DestImage)
LockBuffer(DestBuffer)
Select ChannelFlags
; All channels.
Case 1+2+4
For LoopY = 0 To IHeight-1
YOffset = LoopY*IWidth
For LoopX = 0 To IWidth-1
Offset = (YOffset + LoopX) Shl 2
Y# = PeekFloat(SrcImage\Bank[0], Offset)
Cb# = PeekFloat(SrcImage\Bank[1], Offset)
Cr# = PeekFloat(SrcImage\Bank[2], Offset)
Cb# = Cb# - 0.5
Cr# = Cr# - 0.5
Rf# = Y# + 1.40200*Cr#
Gf# = Y# - 0.34414*Cb# - 0.71414*Cr#
Bf# = Y# + 1.77200*Cb#
R = Rf# * 255
G = Gf# * 255
B = Bf# * 255
If R < 0 Then R = 0
If G < 0 Then G = 0
If B < 0 Then B = 0
If R > 255 Then R = 255
If G > 255 Then G = 255
If B > 255 Then B = 255
RGB = (R Shl 16) Or (G Shl 8) Or B
WritePixelFast LoopX, LoopY, RGB, DestBuffer
Next
Next
; Luminosity channel only.
Case 1
For LoopY = 0 To IHeight-1
YOffset = LoopY*IWidth
For LoopX = 0 To IWidth-1
Offset = (YOffset + LoopX) Shl 2
Y# = PeekFloat(SrcImage\Bank[0], Offset)
R = Y# * 255
If R < 0 Then R = 0
If R > 255 Then R = 255
RGB = (R Shl 16) Or (R Shl 8) Or R
WritePixelFast LoopX, LoopY, RGB, DestBuffer
Next
Next
; Color channels only.
Case 2+4
For LoopY = 0 To IHeight-1
YOffset = LoopY*IWidth
For LoopX = 0 To IWidth-1
Offset = (YOffset + LoopX) Shl 2
Y# = 0.5
Cb# = PeekFloat(SrcImage\Bank[1], Offset)
Cr# = PeekFloat(SrcImage\Bank[2], Offset)
Cb# = Cb# - 0.5
Cr# = Cr# - 0.5
Rf# = Y# + 1.40200*Cr#
Gf# = Y# - 0.34414*Cb# - 0.71414*Cr#
Bf# = Y# + 1.77200*Cb#
R = Rf# * 255
G = Gf# * 255
B = Bf# * 255
If R < 0 Then R = 0
If G < 0 Then G = 0
If B < 0 Then B = 0
If R > 255 Then R = 255
If G > 255 Then G = 255
If B > 255 Then B = 255
RGB = (R Shl 16) Or (G Shl 8) Or B
WritePixelFast LoopX, LoopY, RGB, DestBuffer
Next
Next
End Select
UnlockBuffer(DestBuffer)
; If the version of the program the user is running is the evalulation version, then put a watermark on the final image.
If VERSION = EVALUATION Then Draw_Watermark(DestImage)
; Return a pointer to the new image.
Return DestImage
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This functon blurs a YCC image using a Box blur. The specified image is not modified. A new image is returned.
;
; Radius is the radius of the blur in pixels.
;
; Passes is the number of times the blur should be applied. This improves the quality of the blur.
; Three passes approximates a Gaussian blur very well.
; One or two passes does not generally produce a good blur, but may work okay for small radiuses.
;
; There may be a bug in this function when it is used to blur more than one channel, because the horizontal and vertical passes
; are done for each channel at the same time, and the source and destination get swapped for these. I think.
; -----------------------------------------------------------------------------------------------------------------------------------
Function YCC_Filter_Blur.IMAGE_YCC(Image.IMAGE_YCC, Radius, ChannelFlags=1+2+4, Passes=3, Horizontal=True, Vertical=True)
Local IWidth, IHeight
Local SrcImage.IMAGE_YCC, DestImage.IMAGE_YCC, TempImage.IMAGE_YCC
Local Pass, Channel, LoopX, LoopY
Local YOffset, Offset
Local X, Y
Local A#
Local Avg#
Local WindowSize
IWidth = Image\Width
IHeight = Image\Height
If Radius > IWidth Then Radius = IWidth
If Radius > IHeight Then Radius = IHeight
; It's too complicated to make this code function in such a way that we can avoid copying the source image at the start.
SrcImage = CopyYCCImage(Image, ChannelFlags)
DestImage = CreateYCCImage(Image\Width, Image\Height, ChannelFlags)
For Pass = 1 To Passes
; Loop through each color channel.
For Channel = 0 To 2
; If this channel should be modified...
If ChannelFlags And 2^Channel
; Perform a box blur on each axis of this channel.
If Horizontal = True
For LoopY = 0 To IHeight-1
A# = 0
WindowSize = 0
; Calculate value of first pixel in row, and set up accumulator.
; Accumulate pixels in accumulation window.
For LoopX = 0 To Radius
; If this pixel is within the image,
; add the pixel to the accumulator,
; and increase the size of the accumulation window.
If (LoopX >= 0) And (LoopX < IWidth)
A# = A# + PeekFloat(SrcImage\Bank[Channel], (LoopY*IWidth + LoopX) * 4)
WindowSize = WindowSize + 1
EndIf
Next
; Compute their average.
Avg# = A# / Float(WindowSize)
; Store average in final image.
PokeFloat DestImage\Bank[Channel], LoopY*IWidth * 4, Avg#
; Calculate value of each pixel after the first.
; Caclulate the location of the leftmost and rightmost pixels of the accumulation window.
X1 = -Radius
X2 = Radius
; Move accumulation window left to right across texture.
For LoopX = 1 To IWidth-1
; If the leftmost pixel of the window is inside the image,
; subtract it from the accumulated value of the pixels in the window,
; and reduce the size of the window.
If X1 >= 0
A# = A# - PeekFloat(SrcImage\Bank[Channel], (LoopY*IWidth + X1) * 4)
WindowSize = WindowSize - 1
EndIf
; If the pixel to the right of the window is inside the image,
; add the pixel to the accumulated value of the pixels in the window,
; and increase the size of the window.
If (X2+1) < IWidth
A# = A# + PeekFloat(SrcImage\Bank[Channel], (LoopY*IWidth + (X2+1)) * 4)
WindowSize = WindowSize + 1
EndIf
; Compute the average of pixels in the window.
Avg# = A# / Float(WindowSize)
; Store pixel average in final image.
Offset = (LoopY*IWidth + LoopX) * 4
PokeFloat DestImage\Bank[Channel], Offset, Avg#
; Move the accumulation window one pixel to the right.
X1 = X1 + 1
X2 = X2 + 1
Next
Next ; LoopY
EndIf
If (Horizontal = True) And (Vertical = True)
; Swap source and destination image pointers.
TempImage = DestImage
DestImage = SrcImage
SrcImage = TempImage
EndIf
If Vertical = True
For LoopX = 0 To IWidth-1
A# = 0
WindowSize = 0
; Calculate value of first pixel in column, and set up accumulator.
; Accumulate pixels in accumulation window.
For LoopY = 0 To Radius
; If this pixel is within the image,
; add the pixel to the accumulator,
; and increase the size of the accumulation window.
If (LoopY >= 0) And (LoopY < IHeight)
A# = A# + PeekFloat(SrcImage\Bank[Channel], (LoopY*IWidth + LoopX) * 4)
WindowSize = WindowSize + 1
EndIf
Next
; Compute their average.
Avg# = A# / Float(WindowSize)
; Store average in final image.
PokeFloat DestImage\Bank[Channel], LoopX * 4, Avg#
; Calculate value of each pixel after the first.
; Caclulate the location of the topmost and bottommost pixels of the accumulation window.
Y1 = -Radius
Y2 = Radius
; Move accumulation window top to bottom down texture.
For LoopY = 1 To IHeight-1
; If the topmost pixel of the window is inside the image,
; subtract it from the accumulated value of the pixels in the window,
; and reduce the size of the window.
If Y1 >= 0
A# = A# - PeekFloat(SrcImage\Bank[Channel], (Y1*IWidth + LoopX) * 4)
WindowSize = WindowSize - 1
EndIf
; If the pixel to the bottom of the window is inside the image,
; add the pixel to the accumulated value of the pixels in the window,
; and increase the size of the window.
If (Y2+1) < IHeight
A# = A# + PeekFloat(SrcImage\Bank[Channel], ((Y2+1)*IWidth + LoopX) * 4)
WindowSize = WindowSize + 1
EndIf
; Compute the average of pixels in the window.
Avg# = A# / Float(WindowSize)
; Store pixel average in final image.
Offset = (LoopY*IWidth + LoopX) * 4
PokeFloat DestImage\Bank[Channel], Offset, Avg#
; Move the accumulation window one pixel to the right.
Y1 = Y1 + 1
Y2 = Y2 + 1
Next
Next ; LoopX
EndIf
EndIf
Next ; Channel
; If this is not the last pass:
If Pass < Passes
; Swap source and destination pointers for the next pass.
TempImage = DestImage
DestImage = SrcImage
SrcImage = TempImage
EndIf
Next ; Pass
; Free the source image, and return a pointer to the destination image.
FreeYCCImage(SrcImage)
Return DestImage
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This functon takes a YCC image as input, and outputs a sharpened version.
; It uses a technique similar to unsharp masking, but which uses a median filter instead of a gaussian blur.
; -----------------------------------------------------------------------------------------------------------------------------------
Function YCC_Filter_Unsharp.IMAGE_YCC(Image.IMAGE_YCC, Radius, Strength#=1.0, ChannelFlags=1+2+4)
Local IWidth, IHeight
Local SrcImage.IMAGE_YCC, SrcImageLowpass.IMAGE_YCC, SrcImageHighpass.IMAGE_YCC, DestImage.IMAGE_YCC
Local Channel, LoopX, LoopY
IWidth = Image\Width
IHeight = Image\Height
If Radius > IWidth Then Radius = IWidth
If Radius > IHeight Then Radius = IHeight
SrcImage = Image
DestImage = CreateYCCImage(IWidth, IHeight, ChannelFlags)
; Calculate the low pass and high pass versions of the source image.
SrcImageLowpass = YCC_Filter_Median(SrcImage, Radius, ChannelFlags)
SrcImageHighpass = YCC_Filter_Highpass(SrcImage, SrcImageLowpass, ChannelFlags)
; Subtract the high pass image from the original image's luminosity channel.
; Loop through each pixel in this channel.
For LoopY = 0 To IHeight-1
; Reset the destination pixel offset for this row.
Offset = LoopY*IWidth*4
For LoopX = 0 To IWidth-1
; High pass pixel can be from -1 to 1, but generally will be from -0.05 to 0.05
SrcPixel# = PeekFloat(SrcImage\Bank[0], Offset)
SrcPixelHighpass# = PeekFloat(SrcImageHighpass\Bank[0], Offset)
PokeFloat DestImage\Bank[0], Offset, (SrcPixel# + (SrcPixelHighpass#*Strength#))
; Weighted
;PokeFloat DestImage\Bank[0], Offset, (SrcPixel# + (SrcPixelHighpass#*Strength#)*Abs(SrcPixel#-SrcPixelHighpass#))
Offset = Offset + 4
Next ; LoopX
Next ; LoopY
; Just copy the color channels, no need to sharpen them.
CopyYCCImageTo(SrcImage, DestImage, 2+4)
; Free the low pass and high pass versions of the source image which are no longer needed.
FreeYCCImage(SrcImageLowpass)
FreeYCCImage(SrcImageHighpass)
; Return pointer to the new destination image.
Return DestImage
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This functon takes a YCC image and a LowPass version of that image as input, and outputs a high-pass image.
; OutputPixel = 2*InputPixel - InputLowPassPixel
; -----------------------------------------------------------------------------------------------------------------------------------
Function YCC_Filter_Highpass.IMAGE_YCC(SrcImage.IMAGE_YCC, SrcImageLowpass.IMAGE_YCC, ChannelFlags=1+2+4)
Local IWidth, IHeight
Local DestImage.IMAGE_YCC
Local Channel, LoopX, LoopY
Local SrcPixel#, SrcpixelLowpass#
IWidth = SrcImage\Width
IHeight = SrcImage\Height
DestImage = CreateYCCImage(IWidth, IHeight, ChannelFlags)
; Loop through each color channel.
For Channel = 0 To 2
; If this channel should be modified...
If ChannelFlags And 2^Channel
; Loop through each pixel in this channel.
For LoopY = 0 To IHeight-1
; Reset the destination pixel offset for this row.
Offset = LoopY*IWidth*4
For LoopX = 0 To IWidth-1
SrcPixel# = PeekFloat(SrcImage\Bank[Channel], Offset)
SrcPixelLowpass# = PeekFloat(SrcImageLowpass\Bank[Channel], Offset)
PokeFloat DestImage\Bank[Channel], Offset, (SrcPixel# - SrcPixelLowpass#)
Offset = Offset + 4
Next ; LoopX
Next ; LoopY
EndIf
Next ; Channel
; Return pointer to the new destination image.
Return DestImage
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This functon takes a YCC image as input, and outputs a median filtered version.
; -----------------------------------------------------------------------------------------------------------------------------------
Function YCC_Filter_Median.IMAGE_YCC(Image.IMAGE_YCC, Radius, ChannelFlags=1+2+4)
Local IWidth, IHeight
Local SrcImage.IMAGE_YCC, DestImage.IMAGE_YCC, TempImage.IMAGE_YCC
Local Pass, Channel, LoopX, LoopY
Local Radius2
Local XOffset, YOffset
Local Pixels
Local X, Y
Radius2 = Radius*Radius
IWidth = Image\Width
IHeight = Image\Height
If Radius > IWidth Then Radius = IWidth
If Radius > IHeight Then Radius = IHeight
SrcImage = Image
DestImage = CreateYCCImage(IWidth, IHeight, ChannelFlags)
; Loop through each color channel.
For Channel = 0 To 2
; If this channel should be modified...
If ChannelFlags And 2^Channel
; Loop through each pixel in this channel.
For LoopY = 0 To IHeight-1
; Reset the destination pixel offset for this row.
Offset = LoopY*IWidth*4
For LoopX = 0 To IWidth-1
; Reset the number of pixels in the sampling region.
Pixels = 0
; Get each pixel within the desired radius around this pixel.
For YOffset = -Radius To Radius
For XOffset = -Radius To Radius
; If this pixel is within the desired radius...
If (XOffset*XOffset + YOffset*YOffset) <= Radius2
X = LoopX + XOffset
Y = LoopY + YOffset
; And if this pixel is within the image...
If (X >= 0) And (X < IWidth)
If (Y >= 0) And (Y < IHeight)
; Store the value of this pixel in the median array.
MedianArray#(Pixels) = PeekFloat(SrcImage\Bank[Channel], (Y*IWidth + X) * 4)
; Increment the number of pixels we need to find the median of.
Pixels = Pixels + 1
EndIf
EndIf
EndIf
Next
Next
; Calculate the median value of the pixels in MedianArray#()
; And store the median value in the destination pixel.
PokeFloat DestImage\Bank[Channel], Offset, Median#(Pixels)
; Incremenet the offset by one pixel.
Offset = Offset + 4
Next ; LoopX
Next ; LoopY
EndIf
Next ; Channel
; Return a pointer to the new destination image.
Return DestImage
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This function calculates the average, min, and max of the specified channel in a YCC image.
; -----------------------------------------------------------------------------------------------------------------------------------
Function YCC_CalculateChannelAverageMinMax(SrcImage.IMAGE_YCC, Channel=0)
Local ISize
Local Offset
Local Pixel#, Average#, Min#, Max#
ISize = SrcImage\Width * SrcImage\Height
;Min# = PeekFloat(SrcImage\Bank[Channel], 0)
;Max# = Min#
For Offset = 0 To ISize-1
Pixel# = PeekFloat(SrcImage\Bank[Channel], Offset*4)
Average# = Average# + Pixel#
;If Pixel# < Min# Then Min# = Pixel#
;If Pixel# > Max# Then Max# = Pixel#
Next
Average# = Average# / Float(ISize)
YCC_ChannelAverage# = Average#
;YCC_ChannelMin# = Min#
;YCC_ChannelMax# = Max#
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This function clears a bank with a value as quickly as possible.
; -----------------------------------------------------------------------------------------------------------------------------------
Function ClearBank(Bank, Value=0)
Local Size
Local SizeInt
Local SizeMod
Size = BankSize(Bank)
SizeInt = Size / 4
SizeMod = Size Mod 4
; Fill the bank with as many long ints as possible.
For Offset = 0 To SizeInt-1
PokeInt Bank, Offset*4, Value
Next
; Fill remaining bytes.
For Offset = 0 To SizeMod-1
PokeByte Bank, (Size-4)+Offset, Value
Next
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; These functions use the Wirth median algorithm.
;
; The Wirth median algorithm is one of the fastest median algorithms out there.
; There is one algorithm I know of which is 30% faster, but the code for that one is much more complicated.
;
; This function can work very fast even on large datasets.
; Finding the median of 10,000 values with it is not 1,000 times slower than sorting 10.
; -----------------------------------------------------------------------------------------------------------------------------------
Dim MedianArray#(256)
; -----------------------------------------------------------------------------------------------------------------------------------
; This function returns the median value of an array using the Wirth algorithm.
; If the median is between two values, it returns the lower of the two.
;
; Array_Size is the number of values in the median array you want to be examined.
; -----------------------------------------------------------------------------------------------------------------------------------
Function Median#(Array_Size)
Local i,j,l,m
Local x#, Temp#
Local Nth
Nth = Floor(Array_Size/2.0)
l = 0
m = Array_Size-1
While (l < m)
x# = MedianArray#(Nth)
i = l
j = m
Repeat
While MedianArray#(i) < x#
i = i + 1
Wend
While x# < MedianArray#(j)
j = j - 1
Wend
If i <= j
; Swap MedianArray#(i) and MedianArray#(j)
Temp# = MedianArray#(i)
MedianArray#(i) = MedianArray#(j)
MedianArray#(j) = Temp#
i = i + 1
j = j - 1
EndIf
Until (i > j)
If (j < Nth) Then l = i
If (Nth < i) Then m = j
Wend
Return MedianArray#(Nth)
End Function
; -----------------------------------------------------------------------------------------------------------------------------------
; This function is basically the same as the above, but returns the Nth smallest value of an array using the Wirth algorithm.
; To find the MEDIAN value for example, you would set N to half the size of the array.
; -----------------------------------------------------------------------------------------------------------------------------------
Function Nth_Smallest#(Array_Size, Nth)
Local i,j,l,m
Local x#, Temp#
l = 0
m = Array_Size-1
While (l < m)
x# = MedianArray#(Nth)
i = l
j = m
Repeat
While MedianArray#(i) < x#
i = i + 1
Wend
While x# < MedianArray#(j)
j = j - 1
Wend
If i <= j
; Swap MedianArray#(i) and MedianArray#(j)
Temp# = MedianArray#(i)
MedianArray#(i) = MedianArray#(j)
MedianArray#(j) = Temp#
i = i + 1
j = j - 1
EndIf
Until (i > j)
If (j < Nth) Then l = i
If (Nth < i) Then m = j
Wend
Return MedianArray#(Nth)
End Function
I think I included all the pertinent code there... Anyway, YCC is Luminosity, and two color channels. It is used by JPEG. There's actually a ton of variations on YCC, all defined by what ratios they use RG and B in when squeezing them into the two color channels. I used one which I chose after doing a lot of research that appeared to indicate it was one of the more popular and most accurate ones.
Anyway, you only want to sharpen the luminosity channel, so this will do. The code abover actually uses a custom sharpen algorithm I developed which performs an unsharp filter using a median filter instead of a gaussian filter for one of the steps. This results in a sharp image with no halos. You can try the gaussian filter instead to do a normal unsharp, but I was never able to get results like those you get in photoshop. Primarily because I don't think my guassian filter can do radiuses like 0.3 like I use in photoshop. Btw, that is a VERY fast guassian blur filter there. It's as fast as you're likely to get. It does the blur by using multiple box blurs, and the result after 3 such blurs is the same as a gaussian. A box blur is extremely fast because you only have to read each pixel once even when you are averaging 16 pixels per pixel. I think it does the box blur once in each direction to do the 2d box blur... I forget.
Anyway these are some of the functions I use in my seamless texture genrator. Hopw you can use em. :-)
|