Documentation
¶
Overview ¶
Package prism provides a set of tools for colour management and conversion. Subpackages provide support for encoding/decoding image pixel data in specific colour spaces, and conversions between those spaces.
Example (ConvertAdobeRGBToSRGB) ¶
package main
import (
"fmt"
"github.com/mandykoh/prism"
"github.com/mandykoh/prism/adobergb"
"github.com/mandykoh/prism/srgb"
"image"
"image/png"
"log"
"os"
"path/filepath"
"runtime"
_ "image/jpeg"
_ "image/png"
)
func loadImage(path string) *image.NRGBA {
imgFile, err := os.Open(path)
if err != nil {
panic(err)
}
defer imgFile.Close()
img, _, err := image.Decode(imgFile)
if err != nil {
panic(err)
}
return prism.ConvertImageToNRGBA(img, runtime.NumCPU())
}
func compare(img1, img2 *image.NRGBA, threshold int) float64 {
diffCount := 0
for i := img1.Rect.Min.Y; i < img1.Rect.Max.Y; i++ {
for j := img1.Rect.Min.X; j < img1.Rect.Max.X; j++ {
c1 := img1.NRGBAAt(j, i)
d1 := [4]int{int(c1.R), int(c1.G), int(c1.B), int(c1.A)}
c2 := img2.NRGBAAt(j, i)
d2 := [4]int{int(c2.R), int(c2.G), int(c2.B), int(c2.A)}
diff := 0
for k := range d1 {
if d1[k] > d2[k] {
diff += d1[k] - d2[k]
} else {
diff += d2[k] - d1[k]
}
}
if diff > threshold {
diffCount++
}
}
}
return float64(diffCount) / float64(img1.Rect.Dx()*img1.Rect.Dy())
}
func writeImage(path string, img image.Image) {
_ = os.MkdirAll(filepath.Dir(path), os.ModePerm)
imgFile, err := os.Create(path)
if err != nil {
panic(err)
}
defer imgFile.Close()
err = png.Encode(imgFile, img)
if err != nil {
panic(err)
}
log.Printf("Output written to %s", path)
}
func main() {
referenceImg := loadImage("test-images/pizza-rgb8-srgb.jpg")
inputImg := loadImage("test-images/pizza-rgb8-adobergb.jpg")
convertedImg := image.NewNRGBA(inputImg.Rect)
for i := inputImg.Rect.Min.Y; i < inputImg.Rect.Max.Y; i++ {
for j := inputImg.Rect.Min.X; j < inputImg.Rect.Max.X; j++ {
inCol, alpha := adobergb.ColorFromNRGBA(inputImg.NRGBAAt(j, i))
outCol := srgb.ColorFromXYZ(inCol.ToXYZ())
convertedImg.SetNRGBA(j, i, outCol.ToNRGBA(alpha))
}
}
writeImage("example-output/adobergb-to-srgb.png", convertedImg)
if difference := compare(convertedImg, referenceImg, 5); difference > 0.01 {
fmt.Printf("Images differ by %.2f%% of pixels exceeding difference threshold", difference*100)
} else {
fmt.Printf("Images match")
}
}
Output: Images match
Example (ConvertDisplayP3ToSRGB) ¶
package main
import (
"fmt"
"github.com/mandykoh/prism"
"github.com/mandykoh/prism/displayp3"
"github.com/mandykoh/prism/srgb"
"image"
"image/png"
"log"
"os"
"path/filepath"
"runtime"
_ "image/jpeg"
_ "image/png"
)
func loadImage(path string) *image.NRGBA {
imgFile, err := os.Open(path)
if err != nil {
panic(err)
}
defer imgFile.Close()
img, _, err := image.Decode(imgFile)
if err != nil {
panic(err)
}
return prism.ConvertImageToNRGBA(img, runtime.NumCPU())
}
func compare(img1, img2 *image.NRGBA, threshold int) float64 {
diffCount := 0
for i := img1.Rect.Min.Y; i < img1.Rect.Max.Y; i++ {
for j := img1.Rect.Min.X; j < img1.Rect.Max.X; j++ {
c1 := img1.NRGBAAt(j, i)
d1 := [4]int{int(c1.R), int(c1.G), int(c1.B), int(c1.A)}
c2 := img2.NRGBAAt(j, i)
d2 := [4]int{int(c2.R), int(c2.G), int(c2.B), int(c2.A)}
diff := 0
for k := range d1 {
if d1[k] > d2[k] {
diff += d1[k] - d2[k]
} else {
diff += d2[k] - d1[k]
}
}
if diff > threshold {
diffCount++
}
}
}
return float64(diffCount) / float64(img1.Rect.Dx()*img1.Rect.Dy())
}
func writeImage(path string, img image.Image) {
_ = os.MkdirAll(filepath.Dir(path), os.ModePerm)
imgFile, err := os.Create(path)
if err != nil {
panic(err)
}
defer imgFile.Close()
err = png.Encode(imgFile, img)
if err != nil {
panic(err)
}
log.Printf("Output written to %s", path)
}
func main() {
referenceImg := loadImage("test-images/pizza-rgb8-srgb.jpg")
inputImg := loadImage("test-images/pizza-rgb8-displayp3.jpg")
convertedImg := image.NewNRGBA(inputImg.Rect)
for i := inputImg.Rect.Min.Y; i < inputImg.Rect.Max.Y; i++ {
for j := inputImg.Rect.Min.X; j < inputImg.Rect.Max.X; j++ {
inCol, alpha := displayp3.ColorFromNRGBA(inputImg.NRGBAAt(j, i))
outCol := srgb.ColorFromXYZ(inCol.ToXYZ())
convertedImg.SetNRGBA(j, i, outCol.ToNRGBA(alpha))
}
}
writeImage("example-output/displayp3-to-srgb.png", convertedImg)
if difference := compare(convertedImg, referenceImg, 5); difference > 0.005 {
fmt.Printf("Images differ by %.2f%% of pixels exceeding difference threshold", difference*100)
} else {
fmt.Printf("Images match")
}
}
Output: Images match
Example (ConvertProPhotoRGBToSRGB) ¶
package main
import (
"fmt"
"github.com/mandykoh/prism"
"github.com/mandykoh/prism/ciexyz"
"github.com/mandykoh/prism/prophotorgb"
"github.com/mandykoh/prism/srgb"
"image"
"image/png"
"log"
"os"
"path/filepath"
"runtime"
_ "image/jpeg"
_ "image/png"
)
func loadImage(path string) *image.NRGBA {
imgFile, err := os.Open(path)
if err != nil {
panic(err)
}
defer imgFile.Close()
img, _, err := image.Decode(imgFile)
if err != nil {
panic(err)
}
return prism.ConvertImageToNRGBA(img, runtime.NumCPU())
}
func compare(img1, img2 *image.NRGBA, threshold int) float64 {
diffCount := 0
for i := img1.Rect.Min.Y; i < img1.Rect.Max.Y; i++ {
for j := img1.Rect.Min.X; j < img1.Rect.Max.X; j++ {
c1 := img1.NRGBAAt(j, i)
d1 := [4]int{int(c1.R), int(c1.G), int(c1.B), int(c1.A)}
c2 := img2.NRGBAAt(j, i)
d2 := [4]int{int(c2.R), int(c2.G), int(c2.B), int(c2.A)}
diff := 0
for k := range d1 {
if d1[k] > d2[k] {
diff += d1[k] - d2[k]
} else {
diff += d2[k] - d1[k]
}
}
if diff > threshold {
diffCount++
}
}
}
return float64(diffCount) / float64(img1.Rect.Dx()*img1.Rect.Dy())
}
func writeImage(path string, img image.Image) {
_ = os.MkdirAll(filepath.Dir(path), os.ModePerm)
imgFile, err := os.Create(path)
if err != nil {
panic(err)
}
defer imgFile.Close()
err = png.Encode(imgFile, img)
if err != nil {
panic(err)
}
log.Printf("Output written to %s", path)
}
func main() {
referenceImg := loadImage("test-images/pizza-rgb8-srgb.jpg")
inputImg := loadImage("test-images/pizza-rgb8-prophotorgb.jpg")
adaptation := ciexyz.AdaptBetweenXYYWhitePoints(
prophotorgb.StandardWhitePoint,
srgb.StandardWhitePoint,
)
convertedImg := image.NewNRGBA(inputImg.Rect)
for i := inputImg.Rect.Min.Y; i < inputImg.Rect.Max.Y; i++ {
for j := inputImg.Rect.Min.X; j < inputImg.Rect.Max.X; j++ {
inCol, alpha := prophotorgb.ColorFromNRGBA(inputImg.NRGBAAt(j, i))
xyz := inCol.ToXYZ()
xyz = adaptation.Apply(xyz)
outCol := srgb.ColorFromXYZ(xyz)
convertedImg.SetNRGBA(j, i, outCol.ToNRGBA(alpha))
}
}
writeImage("example-output/prophotorgb-to-srgb.png", convertedImg)
if difference := compare(convertedImg, referenceImg, 5); difference > 0.015 {
fmt.Printf("Images differ by %.2f%% of pixels exceeding difference threshold", difference*100)
} else {
fmt.Printf("Images match")
}
}
Output: Images match
Example (ConvertSRGBToAdobeRGB) ¶
package main
import (
"fmt"
"github.com/mandykoh/prism"
"github.com/mandykoh/prism/adobergb"
"github.com/mandykoh/prism/srgb"
"image"
"os"
"runtime"
_ "image/jpeg"
_ "image/png"
)
func loadImage(path string) *image.NRGBA {
imgFile, err := os.Open(path)
if err != nil {
panic(err)
}
defer imgFile.Close()
img, _, err := image.Decode(imgFile)
if err != nil {
panic(err)
}
return prism.ConvertImageToNRGBA(img, runtime.NumCPU())
}
func compare(img1, img2 *image.NRGBA, threshold int) float64 {
diffCount := 0
for i := img1.Rect.Min.Y; i < img1.Rect.Max.Y; i++ {
for j := img1.Rect.Min.X; j < img1.Rect.Max.X; j++ {
c1 := img1.NRGBAAt(j, i)
d1 := [4]int{int(c1.R), int(c1.G), int(c1.B), int(c1.A)}
c2 := img2.NRGBAAt(j, i)
d2 := [4]int{int(c2.R), int(c2.G), int(c2.B), int(c2.A)}
diff := 0
for k := range d1 {
if d1[k] > d2[k] {
diff += d1[k] - d2[k]
} else {
diff += d2[k] - d1[k]
}
}
if diff > threshold {
diffCount++
}
}
}
return float64(diffCount) / float64(img1.Rect.Dx()*img1.Rect.Dy())
}
func main() {
referenceImg := loadImage("test-images/pizza-rgb8-adobergb.jpg")
inputImg := loadImage("test-images/pizza-rgb8-srgb.jpg")
convertedImg := image.NewNRGBA(inputImg.Rect)
for i := inputImg.Rect.Min.Y; i < inputImg.Rect.Max.Y; i++ {
for j := inputImg.Rect.Min.X; j < inputImg.Rect.Max.X; j++ {
inCol, alpha := srgb.ColorFromNRGBA(inputImg.NRGBAAt(j, i))
outCol := adobergb.ColorFromXYZ(inCol.ToXYZ())
convertedImg.SetNRGBA(j, i, outCol.ToNRGBA(alpha))
}
}
// Output will be written without an embedded colour profile (software used
// to examine this image will assume sRGB unless told otherwise).
//writeImage("example-output/srgb-to-adobergb.png", convertedImg)
if difference := compare(convertedImg, referenceImg, 4); difference > 0.01 {
fmt.Printf("Images differ by %.2f%% of pixels exceeding difference threshold", difference*100)
} else {
fmt.Printf("Images match")
}
}
Output: Images match
Example (LinearisedResampling) ¶
package main
import (
"github.com/mandykoh/prism"
"github.com/mandykoh/prism/srgb"
"golang.org/x/image/draw"
"image"
"image/png"
"log"
"os"
"path/filepath"
"runtime"
_ "image/jpeg"
_ "image/png"
)
func loadImage(path string) *image.NRGBA {
imgFile, err := os.Open(path)
if err != nil {
panic(err)
}
defer imgFile.Close()
img, _, err := image.Decode(imgFile)
if err != nil {
panic(err)
}
return prism.ConvertImageToNRGBA(img, runtime.NumCPU())
}
func writeImage(path string, img image.Image) {
_ = os.MkdirAll(filepath.Dir(path), os.ModePerm)
imgFile, err := os.Create(path)
if err != nil {
panic(err)
}
defer imgFile.Close()
err = png.Encode(imgFile, img)
if err != nil {
panic(err)
}
log.Printf("Output written to %s", path)
}
func main() {
img := loadImage("test-images/checkerboard-srgb.png")
rgba64 := image.NewRGBA64(img.Bounds())
srgb.LineariseImage(rgba64, img, runtime.NumCPU())
resampled := image.NewNRGBA64(image.Rect(0, 0, rgba64.Rect.Dx()/2, rgba64.Rect.Dy()/2))
draw.BiLinear.Scale(resampled, resampled.Rect, rgba64, rgba64.Rect, draw.Src, nil)
rgba := image.NewRGBA(resampled.Rect)
srgb.EncodeImage(rgba, resampled, runtime.NumCPU())
writeImage("example-output/checkerboard-resampled.png", rgba)
}
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ConvertImageToNRGBA ¶ added in v0.27.0
ConvertImageToNRGBA is a convenience function for getting an NRGBA image from any image. If the specified image isn’t already NRGBA, a conversion is performed.
parallelism specifies the maximum degree of parallel processing; a value of 4 indicates that processing may be spread across up to four threads. However, this is not guaranteed as not all conversions are parallelised.
func ConvertImageToRGBA ¶ added in v0.27.0
ConvertImageToRGBA is a convenience function for getting an RGBA image from any image. If the specified image isn’t already RGBA, a conversion is performed.
parallelism specifies the maximum degree of parallel processing; a value of 4 indicates that processing may be spread across up to four threads. However, this is not guaranteed as not all conversions are parallelised.
func ConvertImageToRGBA64 ¶ added in v0.27.0
ConvertImageToRGBA64 is a convenience function for getting an RGBA64 image from any image. If the specified image isn’t already RGBA64, a conversion is performed.
parallelism specifies the maximum degree of parallel processing; a value of 4 indicates that processing may be spread across up to four threads. However, this is not guaranteed as not all conversions are parallelised.
Types ¶
This section is empty.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package adobergb provides support for the Adobe RGB (1998) colour space.
|
Package adobergb provides support for the Adobe RGB (1998) colour space. |
|
Package cielab provides support for the CIE Lab colour space.
|
Package cielab provides support for the CIE Lab colour space. |
|
Package ciexyy provides support for the CIE xyY colour space.
|
Package ciexyy provides support for the CIE xyY colour space. |
|
Package ciexyz provides support for the CIE XYZ colour space.
|
Package ciexyz provides support for the CIE XYZ colour space. |
|
Package displayp3 provides support for the Display P3 colour space.
|
Package displayp3 provides support for the Display P3 colour space. |
|
Package linear provides support for working with linearised colour.
|
Package linear provides support for working with linearised colour. |
|
Package matrix provides support for common matrix maths operations.
|
Package matrix provides support for common matrix maths operations. |
|
Package meta and its subpackages provide support for embedded image metadata.
|
Package meta and its subpackages provide support for embedded image metadata. |
|
autometa
Package autometa provides support for embedded metadata and automatic detection of image formats.
|
Package autometa provides support for embedded metadata and automatic detection of image formats. |
|
binary
Package binary provides common utilities for working with binary data streams.
|
Package binary provides common utilities for working with binary data streams. |
|
icc
Package icc provides support for working with ICC colour profile data.
|
Package icc provides support for working with ICC colour profile data. |
|
jpegmeta
Package jpegmeta provides support for working with embedded JPEG metadata.
|
Package jpegmeta provides support for working with embedded JPEG metadata. |
|
pngmeta
Package pngmeta provides support for working with embedded PNG metadata.
|
Package pngmeta provides support for working with embedded PNG metadata. |
|
webpmeta
Package webpmeta provides support for working with embedded WebP metadata.
|
Package webpmeta provides support for working with embedded WebP metadata. |
|
Package prophotorgb provides support for the Pro Photo RGB colour space.
|
Package prophotorgb provides support for the Pro Photo RGB colour space. |
|
Package srgb provides support for the sRGB colour space.
|
Package srgb provides support for the sRGB colour space. |

