Add random noise to an image in Go
In this article, we are going to demonstrate how to add noise to an image in Golang. A noisy image is the presence of artifacts that do not originate from the original image. This is an artifact in the image that appears as a grainy structure covering the image. In machine learning, this is the addition of unnecessary pixel values that cause the loss of information and image quality.
In this example, we are going to use as many default Go libraries as possible. We are going to create the noisy overlay and merge it on top of the original image. The resulting image will be named result.png
. For this demo project, we will be using a JPEG image file named image.jpg
. This will be the original image on which we will be added the noise overlay on it.
To start with a clean slate, we start off by implementing a method to remove files if exist.
func removeFile(file string) error {
resultRemovalError := os.Remove(file)
if resultRemovalError != nil {
return resultRemovalError
}
return nil
}
For the retrieval of our original image (namely jpeg) we will be implementing the following method:
func getJpegImage(filename string) (image.Image, error) {
imageFile, err := os.Open( fmt.Sprintf("%s.jpg",filename))
if err != nil {
return nil, errors.New(fmt.Sprintf("failed to open original file: %s", err))
}
decodedImage, err := jpeg.Decode(imageFile)
if err != nil {
return nil, errors.New(fmt.Sprintf("failed to decode original image: %s", err))
}
defer func(image_file *os.File) {
_ = image_file.Close()
}(imageFile)
return decodedImage,nil
}
To accomplish the noisy image, we will be implementing a method to create a noisy overlay.
func createNoisyOverLay(backgroundImage image.Image, opacity int) (image.Image, error) {
var requested_opacity = uint8(0)
if opacity > 0 && opacity < 256 {
requested_opacity = uint8(math.Round(float64(opacity) * 2.56))
}
imageBound := backgroundImage.Bounds()
imageWidth := imageBound.Dx()
imageHeight := imageBound.Dy()
myImage := image.NewRGBA(image.Rect(0, 0, imageWidth, imageHeight))
for p := 0; p < imageWidth*imageHeight; p++ {
pixelOffset := 4 * p
myImage.Pix[0+pixelOffset] = uint8(rand.Intn(256))
myImage.Pix[1+pixelOffset] = uint8(rand.Intn(256))
myImage.Pix[2+pixelOffset] = uint8(rand.Intn(256))
myImage.Pix[3+pixelOffset] = requested_opacity
}
tempNoisyImageOutputFile, png_err := os.Create("temp_noisy_image.png")
if png_err != nil {
log.Fatal(png_err)
}
encodingError := png.Encode(tempNoisyImageOutputFile, myImage)
if encodingError != nil {
return nil, encodingError
}
tempNoisyImage, openImageError := os.Open("temp_noisy_image.png")
if openImageError != nil {
return nil, openImageError
}
result, pngDecodingError := png.Decode(tempNoisyImage)
if pngDecodingError != nil {
return nil, pngDecodingError
}
defer func(tempNoisyImage *os.File) {
_ = tempNoisyImage.Close()
}(tempNoisyImage)
// remove temp_noisy_image.png using Remove() function
removalError := removeFile("temp_noisy_image.png")
if removalError != nil {
return nil, removalError
}
return result, nil
}
After creating the noise overlay image we need to merge it on top of the original image. In the following method, we will be drawing the noisy overlay over the original source (in this case the original image).
func mergeAndCreateNoisyImage(originalImage image.Image, noiseOverlayImage image.Image) error {
offset := image.Pt(0, 0)
originalImageBounds := originalImage.Bounds()
noisyImage := image.NewRGBA(originalImageBounds)
draw.Draw(noisyImage, originalImageBounds, originalImage, image.Point{}, draw.Src)
draw.Draw(noisyImage, noiseOverlayImage.Bounds().Add(offset), noiseOverlayImage, image.Point{}, draw.Over)
resultImage, err := os.Create("result.png")
if err != nil {
return errors.New(fmt.Sprintf("failed to create result image: %s", err))
}
pngEncodingError := png.Encode(resultImage, noisyImage)
if pngEncodingError != nil {
return pngEncodingError
}
defer func(third *os.File) {
_ = third.Close()
}(resultImage)
return nil
}
Lastly, in the main function, we will implement the entire process by retrieving our original image in the project. You can also modify the project based on your personal preference to retrieve images based on the command argument for instance.
func main() {
resultRemovalError := removeFile("result.png")
if resultRemovalError != nil {
fmt.Println(resultRemovalError)
}
originalImage, err := getJpegImage("image")
if err != nil {
log.Fatal(err)
}
noisyOverlayImage, secondError := createNoisyOverLay(originalImage, 50)
if secondError != nil {
log.Fatal(secondError)
}
resultingImageError := mergeAndCreateNoisyImage(originalImage,noisyOverlayImage)
if resultingImageError != nil {
log.Fatal(resultingImageError)
}
}
The result will appear as result.png locally in the project if everything went well.
Visit our GitHub repository for the project source code and clone our repository for all future projects.
Follow us:
Good post. I learn something totally new and challenging on blogs I stumbleupon every day. It will always be interesting to read through articles from other writers and use a little something from other sites.