Konvertierung zwischen PB-Image, RGB- und YCbCr-Raum

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8812
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Konvertierung zwischen PB-Image, RGB- und YCbCr-Raum

Beitrag von NicTheQuick »

Hallo Leute,

mir war gestern und heute etwas langweilig und da hab ich mal diese hübsche Procedures geschrieben, mit denen man PB-Images möglichst schnell und Cache-optimiert mittels 'DrawingBuffer()' und passenden Konvertierungsfunktionen für das interne Pixelformat in reine 2D-RGB- oder 2D-YCbCr-Arrays konvertieren kann.

Dabei ist ein Beispiel, das ein ausgewähltes JPG öffnet und die einzelnen Farbkanäle als neue JPGs mit passender Endung im selben Verzeichnis abspeichert.
Viel Spaß damit und wer Fehler findet -> Kommentar!

Achja, als Testbild bietet sich zum Beispiel dieses hier aus Wikimedia an: Barns grand tetons.jpg
Am Ende sollte diese Aufteilung heraus kommen: Barns grand tetons YCbCr separation.jpg

Code: Alles auswählen

EnableExplicit

Prototype.l getDrawingBufferRGB(*address.Integer)
Prototype setDrawingBufferRGB(*address.integer, rgb.l)

Procedure.l getDrawingBufferRGB_8Bits(*address.Integer)	;RRRGGGBB
	Protected g.a = PeekA(*address\i)
	*address\i + 1
	
	ProcedureReturn RGB((g >> 5) << 5, ((g >> 2) & $7) << 5, (g & $7) << 6)
EndProcedure
Procedure.l getDrawingBufferRGB_15Bits(*address.Integer) ;0RRRRRGG GGGBBBBB
	Protected g.u = PeekU(*address\i)
	*address\i + 2
	
	ProcedureReturn RGB((g >> 10) << 3, ((g >> 5) & $1F) << 3, (g & $1F) << 3)
EndProcedure
Procedure.l getDrawingBufferRGB_16Bits(*address.Integer) ;RRRRRGGG GGGBBBBB
	Protected g.u = PeekU(*address\i)
	*address\i + 2
	
	ProcedureReturn RGB((g >> 11) << 3, ((g >> 5) & $3F) << 2, (g & $1F) << 3)
EndProcedure
Procedure.l getDrawingBufferRGB_24Bits_RGB(*address.Integer) ;RRRRRRRR GGGGGGGG BBBBBBBB
	Protected r.a = PeekA(*address\i)
	Protected g.a = PeekA(*address\i + 1)
	Protected b.a = PeekA(*address\i + 2)
	*address\i + 3
	
	ProcedureReturn RGB(r, g, b)
EndProcedure
Procedure.l getDrawingBufferRGB_24Bits_BGR(*address.Integer) ;BBBBBBBB GGGGGGGG RRRRRRRR
	Protected r.a = PeekA(*address\i)
	Protected g.a = PeekA(*address\i + 1)
	Protected b.a = PeekA(*address\i + 2)
	*address\i + 3
	
	ProcedureReturn RGB(b, g, r)
EndProcedure
Procedure.l getDrawingBufferRGB_32Bits_RGBA(*address.Integer) ;RRRRRRRR GGGGGGGG BBBBBBBB 00000000
	Protected r.a = PeekA(*address\i)
	Protected g.a = PeekA(*address\i + 1)
	Protected b.a = PeekA(*address\i + 2)
	Protected a.a = PeekA(*address\i + 3)
	*address\i + 4
	
	ProcedureReturn RGB(r, g, b)
EndProcedure
Procedure.l getDrawingBufferRGB_32Bits_BGRA(*address.Integer) ;BBBBBBBB GGGGGGGG RRRRRRRR 00000000
	Protected b.a = PeekA(*address\i)
	Protected g.a = PeekA(*address\i + 1)
	Protected r.a = PeekA(*address\i + 2)
	Protected a.a = PeekA(*address\i + 3)
	*address\i + 4
	
	ProcedureReturn RGB(r, g, b)
EndProcedure
Procedure.i getDrawingBufferRGB(pixelFormat.i)
	pixelFormat & ~#PB_PixelFormat_ReversedY
	Select pixelFormat
		Case #PB_PixelFormat_15Bits
			ProcedureReturn @getDrawingBufferRGB_15Bits()
		Case #PB_PixelFormat_16Bits
			ProcedureReturn @getDrawingBufferRGB_16Bits()
		Case #PB_PixelFormat_24Bits_BGR
			ProcedureReturn @getDrawingBufferRGB_24Bits_BGR()
		Case #PB_PixelFormat_24Bits_RGB
			ProcedureReturn @getDrawingBufferRGB_24Bits_RGB()
		Case #PB_PixelFormat_32Bits_BGR
			ProcedureReturn @getDrawingBufferRGB_32Bits_BGRA()
		Case #PB_PixelFormat_32Bits_RGB
			ProcedureReturn @getDrawingBufferRGB_32Bits_RGBA()
		Case #PB_PixelFormat_8Bits
			ProcedureReturn @getDrawingBufferRGB_8Bits()
	EndSelect
	ProcedureReturn 0
EndProcedure

Procedure setDrawingBufferRGB_8Bits(*address.Integer, rgb.l) ;RRRGGGBB
	PokeA(*address\i, (((Red(rgb) >> 5) << 5) | ((Green(rgb) >> 5) << 2) | (Blue(rgb) >> 6)))
	*address\i + 1
EndProcedure
Procedure setDrawingBufferRGB_15Bits(*address.Integer, rgb.l) ;0RRRRRGG GGGBBBBB
	PokeU(*address\i, (((Red(rgb) >> 3) << 10) | ((Green(rgb) >> 3) << 5) | (Blue(rgb) >> 3)))
	*address\i + 2
EndProcedure
Procedure setDrawingBufferRGB_16Bits(*address.Integer, rgb.l) ;RRRRRGGG GGGBBBBB
	PokeU(*address\i, (((Red(rgb) >> 3) << 11) | ((Green(rgb) >> 2) << 5) | (Blue(rgb) >> 3)))
	*address\i + 2
EndProcedure
Procedure setDrawingBufferRGB_24Bits_RGB(*address.Integer, rgb.l) ;RRRRRRRR GGGGGGGG BBBBBBBB
	PokeA(*address\i, Red(rgb))
	PokeA(*address\i + 1, Green(rgb))
	PokeA(*address\i + 2, Blue(rgb))
	*address\i + 3
EndProcedure
Procedure setDrawingBufferRGB_24Bits_BGR(*address.Integer, rgb.l) ;BBBBBBBB GGGGGGGG RRRRRRRR
	PokeA(*address\i, Blue(rgb))
	PokeA(*address\i + 1, Green(rgb))
	PokeA(*address\i + 2, Red(rgb))
	*address\i + 3
EndProcedure
Procedure setDrawingBufferRGB_32Bits_RGBA(*address.Integer, rgba.l) ;RRRRRRRR GGGGGGGG BBBBBBBB 00000000
	PokeA(*address\i, Red(rgba))
	PokeA(*address\i + 1, Green(rgba))
	PokeA(*address\i + 2, Blue(rgba))
	PokeA(*address\i + 3, Alpha(rgba))
EndProcedure
Procedure setDrawingBufferRGB_32Bits_BGRA(*address.Integer, rgba.l) ;BBBBBBBB GGGGGGGG RRRRRRRR 00000000
	PokeA(*address\i, Blue(rgba))
	PokeA(*address\i + 1, Green(rgba))
	PokeA(*address\i + 2, Red(rgba))
	PokeA(*address\i + 3, Alpha(rgba))
EndProcedure
Procedure.i setDrawingBufferRGB(pixelFormat.i)
	pixelFormat & ~#PB_PixelFormat_ReversedY
	Select pixelFormat
		Case #PB_PixelFormat_15Bits
			ProcedureReturn @setDrawingBufferRGB_15Bits()
		Case #PB_PixelFormat_16Bits
			ProcedureReturn @setDrawingBufferRGB_16Bits()
		Case #PB_PixelFormat_24Bits_BGR
			ProcedureReturn @setDrawingBufferRGB_24Bits_BGR()
		Case #PB_PixelFormat_24Bits_RGB
			ProcedureReturn @setDrawingBufferRGB_24Bits_RGB()
		Case #PB_PixelFormat_32Bits_BGR
			ProcedureReturn @setDrawingBufferRGB_32Bits_BGRA()
		Case #PB_PixelFormat_32Bits_RGB
			ProcedureReturn @setDrawingBufferRGB_32Bits_RGBA()
		Case #PB_PixelFormat_8Bits
			ProcedureReturn @setDrawingBufferRGB_8Bits()
	EndSelect
	ProcedureReturn 0
EndProcedure

Structure YCbCr
	y.a
	cb.a
	cr.a
EndStructure
Structure RGB
	r.a
	g.a
	b.a
EndStructure
Structure YCbCrImage
	width.i
	height.i
	Array img.YCbCr(0, 0)
EndStructure
Structure RGBImage
	width.i
	height.i
	Array img.RGB(0, 0)
EndStructure
Procedure convertRGB2YCbCr(*in.RGBImage, *out.YCbCrImage)
	Protected y.i, x.i, yc.f, cb.f, cr.f
	
	Dim *out\img(*in\height, *in\width)
	*out\width = *in\width
	*out\height = *in\height
	
	For y = 0 To *in\height - 1
		For x = 0 To *in\width - 1
			With *in\img(y, x)
				yc =         (0.299    * \r) + (0.587    * \g) + (0.114    * \b)
				cb = 127.5 - (0.168736 * \r) - (0.331264 * \g) + (0.5      * \b)
				cr = 127.5 + (0.5      * \r) - (0.418688 * \g) - (0.081312 * \b)
			EndWith
			With *out\img(y, x)
				\y  = Bool(yc >= 0.0) * yc - Bool(yc >= 255.5)
				\cb = Bool(cb >= 0.0) * cb - Bool(cb >= 255.5)
				\cr = Bool(cr >= 0.0) * cr - Bool(cr >= 255.5)
			EndWith
		Next
	Next
	
	ProcedureReturn #True
EndProcedure

Procedure convertYCbCr2RGB(*in.YCbCrImage, *out.RGBImage)
	Protected y.i, x.i, r.f, g.f, b.f
	
	Dim *out\img(*in\height, *in\width)
	*out\width = *in\width
	*out\height = *in\height
	
	For y = 0 To *in\height - 1
		For x = 0 To *in\width - 1
			With *in\img(y, x)
				r = (1.0 * \y) - (0.00000121889 * (\cb - 127.5)) + (1.402         * (\cr - 127.5))
				g = (1.0 * \y) - (0.344136      * (\cb - 127.5)) - (0.714136      * (\cr - 127.5))
				b = (1.0 * \y) + (1.772         * (\cb - 127.5)) + (0.00000406298 * (\cr - 127.5))
			EndWith
			With *out\img(y, x)
				\r = Bool(r >= 0.0) * r - Bool(r >= 255.5)
				\g = Bool(g >= 0.0) * g - Bool(g >= 255.5)
				\b = Bool(b >= 0.0) * b - Bool(b >= 255.5)
			EndWith
		Next
	Next
	
	ProcedureReturn #True
EndProcedure

Procedure convertImg2RGB(in.i, *out.RGBImage)
	Protected x.i, y.i = 0, yDir.i = 1
	Protected color.l, pixelFormat.i, getRGB.getDrawingBufferRGB
	Protected *pixelLine, *pixel
	
	If Not IsImage(in)
		ProcedureReturn #False
	EndIf
	
	*out\width = ImageWidth(in)
	*out\height = ImageHeight(in)
	Dim *out\img(*out\height, *out\width)
	
	If StartDrawing(ImageOutput(in))
		*pixelLine = DrawingBuffer()
		pixelFormat = DrawingBufferPixelFormat()
		getRGB = getDrawingBufferRGB(pixelFormat)
		If pixelFormat & #PB_PixelFormat_ReversedY
			y = *out\height - 1
			yDir = -1
		EndIf
		While y >= 0 And y < *out\height
			*pixel = *pixelLine
			For x = 0 To *out\width - 1
				With *out\img(y, x)
					color = getRGB(@*pixel)
					\r = Red(color)
					\g = Green(color)
					\b = Blue(color)
				EndWith
			Next
			y + yDir
			*pixelLine + DrawingBufferPitch()
		Wend
		StopDrawing()
	Else
		ProcedureReturn #False
	EndIf
	ProcedureReturn #True
EndProcedure

Procedure convertRGB2Img(*in.RGBImage, out.i)
	Protected x.i, y.i = 0, yDir.i = 1
	Protected color.l, pixelFormat.i, setRGB.setDrawingBufferRGB
	Protected *pixelLine, *pixel
	
	If Not IsImage(out)
		ProcedureReturn #False
	EndIf
	
	If *in\width <> ImageWidth(out) Or *in\height <> ImageHeight(out)
		ResizeImage(out, *in\width, *in\height, #PB_Image_Raw)
	EndIf
	
	If StartDrawing(ImageOutput(out))
		*pixelLine = DrawingBuffer()
		pixelFormat = DrawingBufferPixelFormat()
		setRGB = setDrawingBufferRGB(pixelFormat)
		If pixelFormat & #PB_PixelFormat_ReversedY
			y = *in\height - 1
			yDir = -1
		EndIf
		While y >= 0 And y < *in\height
			*pixel = *pixelLine
			For x = 0 To *in\width - 1
				With *in\img(y, x)
					setRGB(@*pixel, RGB(\r, \g, \b))
				EndWith
			Next
			y + yDir
			*pixelLine + DrawingBufferPitch()
		Wend
		StopDrawing()
	Else
		ProcedureReturn #False
	EndIf
	
	ProcedureReturn #True
EndProcedure

Procedure convertImg2YCbCr(in.i, *out.YCbCr)
	Protected rgbImg.RGBImage
	If convertImg2RGB(in, rgbImg)
		ProcedureReturn convertRGB2YCbCr(rgbImg, *out)
	EndIf
	ProcedureReturn #False
EndProcedure

Procedure convertYCbCr2Img(*in.YCbCr, out.i)
	Protected rgbImg.RGBImage
	If convertYCbCr2RGB(*in, rgbImg)
		ProcedureReturn convertRGB2Img(rgbImg, out)
	EndIf
	ProcedureReturn #False
EndProcedure

Procedure setYCbCr(*in.YCbCrImage, y.i = #PB_Ignore, cb.i = #PB_Ignore, cr.i = #PB_Ignore)
	Protected xp.i, yp.i
	With *in
		For yp = 0 To \height - 1
			For xp = 0 To \width - 1
				If y <> #PB_Ignore
					\img(yp, xp)\y = y
				EndIf
				If cb <> #PB_Ignore
					\img(yp, xp)\cb = cb
				EndIf
				If cr <> #PB_Ignore
					\img(yp, xp)\cr = cr
				EndIf
			Next
		Next
	EndWith
EndProcedure

;---------------------------------------------

Procedure.s addSuffixToPath(path.s, suffix.s)
	Protected filePart.s = GetFilePart(path)
	Protected extPart.s = GetExtensionPart(path)
	
	ProcedureReturn GetPathPart(path) + Left(filePart, Len(filePart) - Len(extPart) - 1) + suffix + "." + extPart
EndProcedure
UseJPEGImageDecoder()
UseJPEGImageEncoder()

Define hImg.i, file.s, newFile.s, rgbImg.RGBImage, ycbcrImg.YCbCrImage
Define.i time

file.s = OpenFileRequester("JPG-Datei wählen...", GetHomeDirectory(), "JPG-Dateien (*.jpg)|*.jpg|Alle Dateien|*.*", 0)

time = ElapsedMilliseconds()

hImg = LoadImage(#PB_Any, file)
If Not hImg
	MessageRequester("Fehler!", "Konnte Bild nicht öffnen.")
	End
EndIf

DisableDebugger

;Konvertiere PB-Image in RGB-Image
convertImg2RGB(hImg, rgbImg)

;Konvertiere RGB-Image in YCbCr-Image nach JPG-Standard
convertRGB2YCbCr(rgbImg, ycbcrImg)

;Ignoriere Cb- und Cr-Kanal
setYCbCr(ycbcrImg, #PB_Ignore, 128, 128)

;Konvertiere zurück zu Image
convertYCbCr2Img(ycbcrImg, hImg)

;Speichere
newFile = addSuffixToPath(file, "_Y")
If Not SaveImage(hImg, newFile, #PB_ImagePlugin_JPEG)
	MessageRequester("Fehler!", "Bild konnte nicht gespeichert werden: " + newFile)
EndIf

;Tu das selbe für die anderen Kanäle
convertRGB2YCbCr(rgbImg, ycbcrImg)
setYCbCr(ycbcrImg, 128, #PB_Ignore, 128)
convertYCbCr2Img(ycbcrImg, hImg)
newFile = addSuffixToPath(file, "_Cb")
If Not SaveImage(hImg, newFile, #PB_ImagePlugin_JPEG)
	MessageRequester("Fehler!", "Bild konnte nicht gespeichert werden: " + newFile)
EndIf

convertRGB2YCbCr(rgbImg, ycbcrImg)
setYCbCr(ycbcrImg, 128, 128, #PB_Ignore)
convertYCbCr2Img(ycbcrImg, hImg)
newFile = addSuffixToPath(file, "_Cr")
If Not SaveImage(hImg, newFile, #PB_ImagePlugin_JPEG)
	MessageRequester("Fehler!", "Bild konnte nicht gespeichert werden: " + newFile)
EndIf

time = ElapsedMilliseconds() - time

MessageRequester("Hinweis", "Konvertierung beendet." + #CRLF$ + "Benötigte Zeit: " + time + " ms")

EnableDebugger
Benutzeravatar
RSBasic
Admin
Beiträge: 8047
Registriert: 05.10.2006 18:55
Wohnort: Gernsbach
Kontaktdaten:

Re: Konvertierung zwischen PB-Image, RGB- und YCbCr-Raum

Beitrag von RSBasic »

Cool, danke fürs Teilen. :allright:
Aus privaten Gründen habe ich leider nicht mehr so viel Zeit wie früher. Bitte habt Verständnis dafür.
Bild
Bild
Antworten