I made a kind of simple image processing tool in Go language.

I am a beginner in Go language. My usual habitat is JS / TS + Java, but I am trying Go language from this year because I want to be better at processing server-side languages. The reason for the challenge is "modern, multi-threaded, container-related activity".

After all, the style of actually making something and learning it suits me, so this time with the theme of "image processing", a tool that can be used as both CLI and API yellow-high5 / pictar I made (: //github.com/yellow-high5/pictar). Image processing is not as advanced as using CNN for image recognition.

Go standard image package

First of all, I briefly checked how Go standard image package works.

Point,Rectangle

The coordinate point Point is composed of X and Y of int, and the rectangular area Rectangle is composed of Min and Max of Point.

image.png

Color

--Alpha ... Transparency --CMYK ... Cyan, magenta, yellow, black (Key Plate) color expression method --RGB ... How to express the colors shown in red, green, and blue --Gray .. If the gray scale is RGB, all three values will be the same.

image.NRGBA In this structure, the image is treated as a one-dimensional array ** that repeats ** Red, Green, Blue, Alpha with Pix. Stride stands for ** the size of one horizontal row of the image **.

image.png

Read the imaging library

People who are not specialized in image processing cannot understand difficult formulas, so we use the library. This time, we will use disintegration / imaging. The source itself is also concise and easy to read. The functions can be as follows.

Image processing

--adjust ... grayscale, inversion, contrast, saturation --convolution ... 3x3 compression, 5x5 compression --effects ... Blur, sharp --Histogram (normalized histogram) ... Expresses brightness in an array of 256 (16x16) --resize ... resize, crop, scale (fit), thumbnail --transform ... Flip, Transpose, Rotate

Auxiliary utility

--io ... Read image, open image, write image, save image, read image orientation (EXIF flag), convert, modify --scanner ... Read the area specified by the rectangle --tools ... Create new image, copy, paste, transparent --utils ... parallel, other utilities

Extra: Glossary of image processing

I felt that it was necessary to understand some image processing terms in order to make a tool, so I will organize my knowledge.

-HSV Color Space ... Hue, Saturation A component space consisting of (Saturation) and lightness (Value). It seems to be more intuitive and easier to understand than the RGB space, which is determined by the mixture of primary colors. -HLS Color Space ... Hue, Brightness ( A component space consisting of three components: Lightness and Saturation. Similar to HSV space. -Saturation ... Higher image darker, lower image color Image that becomes thinner. -[Contrast](https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%83%88%E3%83%A9%E3%82%B9%E3 % 83% 88 #% E5% 86% 99% E7% 9C% 9F% E6% A9% 9F% E3% 80% 81% E6% 98% A0% E5% 83% 8F% E6% A9% 9F% E5% 99% A8) ... When it is high, the contrast of the image is clear, and when it is low, the image is vague. -Brightness ... An image that becomes whitish when it is high and blackish when it is low. -Gamma correction ... straightforward proportion RGB correction method that matches human visual characteristics, not relationships. -[Gaussian Blur](https://ja.wikipedia.org/wiki/%E3%82%AC%E3%82%A6%E3%82%B7%E3%82%A2%E3%83%B3% E3% 81% BC% E3% 81% 8B% E3% 81% 97) ... The sigma value corresponds to the amount of blurring. -[Sigmoid Function](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%B0%E3%83%A2%E3%82%A4%E3%83%89% E9% 96% A2% E6% 95% B0) ... In Deep Learning, it was a familiar function to calculate "the probability that the number written in this image is 1", but in image processing contrast Seems to be used to adjust.

Parallel processing

disintegration / imaging uses concurrency when processing images. This seems to allow relatively fast processing. Some people are measuring the speed of image processing, so if you want to know more about it, I think you should refer to this.

Comparison of image processing performance in OpenCV, GoCV, Go language-ZOZO Technologies TECH BLOG

So what kind of concurrency is done in goroutine in this library? The hint was in the parallel function below.

imaging/utils.go


// parallel processes the data in separate goroutines.
func parallel(start, stop int, fn func(<-chan int)) {
	count := stop - start
	if count < 1 {
		return
	}

	procs := runtime.GOMAXPROCS(0)
	limit := int(atomic.LoadInt64(&maxProcs))
	if procs > limit && limit > 0 {
		procs = limit
	}
	if procs > count {
		procs = count
	}

	c := make(chan int, count)
	for i := start; i < stop; i++ {
		c <- i
	}
	close(c)

	var wg sync.WaitGroup
	for i := 0; i < procs; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			fn(c)
		}()
	}
	wg.Wait()
}

** count ** is the number of operations that need to be performed. In the case of image processing, this corresponds to the number of pixels and one horizontal row of images. ** procs ** is the number of processes to be executed concurrently. (The default is the number of CPUs. If you specify the number of goroutines to be processed simultaneously with ** limit **, limit will be adopted.)

The count number of input values is sent to the channel, and the procs number of goroutine receives the input value from the channel and processes it in parallel with the passed function.

Below is a table showing how imaging.FlipH (the process of flipping the left and right of an image) processes in parallel and outputs it. I tried to draw a picture.

image.png

The code that realizes the above picture


func FlipH(img image.Image) *image.NRGBA {
	src := newScanner(img)
	dstW := src.w
	dstH := src.h
	rowSize := dstW * 4
	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
    //Let the parallel processing work be done for the number of image heights
	parallel(0, dstH, func(ys <-chan int) {
        //Invert the pixel group for one line
		for dstY := range ys {
            //Place each pixel in the place at the time of inversion
			i := dstY * dst.Stride
			srcY := dstY
			src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
			reverse(dst.Pix[i : i+rowSize])
		}
	})
	return dst
}

CLI tool with cobra

Wrap these libraries and create a CLI using cobra. First of all, when designing the CLI, let's identify the subcommands and options to be executed by that command. You should also consider making the options globally applicable.

Basically, it is created with the builder pattern in the design pattern. As a reference for design, it is also handled in the achievements of cobra, but I tried to imitate Hugo.

hugo/commands GitHub

In Hugo, when defining a subcommand, define it as follows.

When defining a subcommand called "hoge"


package commands


//The structure defines options
type hogeCmd struct {
  *baseBuilderCmd

  /*Writing options*/
  ...
}

//Returning options in Command Builder
func (b *commandsBuilder) newHogeCmd() *hogeCmd {
  //Define optional empty interface
  cc := &hogeCmd{}
  
  //Command definition with cobra
  cmd := &cobra.Command{
		Use:   "hoge",
		Short: "Short Description",
		Long: `Long Description`,
    RunE: func(cmd *cobra.Command, args []string) error {...},
	}
  //If there is a sub-subcommand, define it
  cmd.AddCommand(...)
  
  //Define options as flags
  cmd.Flags().StringVarp(...)
  
  //Register the builder
  cc.baseBuilderCmd = b.newBuilderBasicCmd(cmd)
  
  return cc
}

Add the necessary subcommands to the command builder and you're done. You can get a better idea of the command builder design by looking at this file (https://github.com/gohugoio/hugo/blob/master/commands/commands.go).

Image processing server with Gin

When I POSTed an image via http, I created a function to process the image and save it in the object storage (S3 this time). The processing content is a mechanism to read from the setting file. It was implemented assuming the functions that can be used for registering profile images and creating thumbnails in the app.

Gin logic

I read Gin's GoDoc, which is a web framework made by Go, and made a rough picture. It's broken quite a bit, but if you simplify it, it looks like this. It seems that you are creating logic that returns an HTTP response from an HTTP request by connecting the processes with HandlerFunc (middleware).

↓ Image image.png

-** Engine ** ... Framework instance (generated by gin.Default () or gin.New ()) -** Context ** ... The most important part. Holds information from the received request to the return of the response. -** HandlerFunc ** ... Processing logic (various processes can be described from D file processing to logging) -** HandlersChain ** ... An array of processing logic. A chain that creates a series of processing flows. -** RouterGroup ** ... Connecting processing logic and URI endpoints

Expressing HTTP processing in a middleware chain is very similar to Express in Node.js.

File upload to S3

I've derailed, but let's get back to the main file upload process. The processing flow is roughly 3 steps.

  1. Read the image from the client specified in the HTTP request body and save it in the file system of the server.
  2. Process the read file with the imaging library.
  3. Upload the processed image file to S3 and return the success status.

I referred to the following for uploading files to Go. Please refer to Amazon Web Services --Go SDK for detailed settings.

Upload files to S3 in Go language (golang) \ | Developers.IO

If you use viper, you can go out to the configuration file (config.json etc.) of detailed settings such as object storage connection settings and image save name. is.

Summary

During my research, I found an image processing server made by Go, which is also introduced in Gin's achievements.

This is better designed than a tool for beginners like me. I decided to use this without hesitation when making it with a service implementation. Learning will deepen if you find a solution to what you were trying to make.

For the time being, I felt that my challenge was to be able to master Go's standard packages and concurrency. Go packages have a good outlook and I'm not too tired so I think I can train for a while.

Recommended Posts

I made a kind of simple image processing tool in Go language.
I wrote a CLI tool in Go language to view Qiita's tag feed with CLI
I made a simple typing game with tkinter in Python
I made a dot picture of the image of Irasutoya. (part1)
I made a dot picture of the image of Irasutoya. (part2)
Created gomi, a trash can tool for rm in Go language
[Python] I made an image viewer with a simple sorting function.
Read the config file in Go language! Introducing a simple sample
I made a payroll program in Python!
I made a browser automatic stamping tool.
I created a password tool in Python.
I made a simple blackjack with Python
I made a program to check the size of a file in Python
I made a mistake in fetching the hierarchy with MultiIndex of pandas
I made go language for api and minimum configuration of react for front
I made a tool to estimate the execution time of cron (+ PyPI debut)
I made a useful tool for Digital Ocean
I made a tool to notify Slack of Connpass events and made it Terraform
[Python] [Natural language processing] I tried Deep Learning ❷ made from scratch in Japanese ①
I made an appdo command to execute a command in the context of the app
I made a router config collection tool Config Collecor
I made a tool to compile Hy natively
I made a module in C language to filter images loaded by Python
I wrote the hexagonal architecture in go language
I made a tool to automatically back up the metadata of the Salesforce organization
I made a tool to get new articles
I made a simple RSS reader ~ C edition ~
I tried simple image processing with Google Colaboratory.
Create a web server in Go language (net/http) (2)
I made a Caesar cryptographic program in Python.
Unbearable shortness of Attention in natural language processing
I made a QR code image with CuteR
After doing 100 language processing knock 2015, I got a lot of basic Python skills Chapter 1
Image processing with Python (I tried binarizing it into a mosaic art of 0 and 1)
I made a prime number generation program in Python
100 image processing knocks !! (021-030) I want to take a break ...
A list of stumbling blocks in Django's image upload
I made a user management tool for Let's Chat
I made a vim learning game "PacVim" with Go
I made a cleaning tool for Google Container Registry
I made a threshold change box of Pepper's Dialog
I made a script to put a snippet in README.md
Performance verification of data preprocessing in natural language processing
Create a web server in Go language (net / http) (1)
I made a prime number generation program in Python 2
Display a histogram of image brightness values in python
I made a repeating text data generation tool "rpttxt"
I used bugspots, a bug prediction tool in mercurial
〇✕ I made a game
Features of Go language
I tried to automatically generate OGP of a blog made with Hugo with tcardgen made by Go
I made a tool to get the answer links of OpenAI Gym all at once
I made a function to crop the image of python openCV, so please use it.
I made a tool to automatically generate a simple ER diagram from the CREATE TABLE statement
[Natural language processing] I tried to visualize the remarks of each member in the Slack community
I made a LINE BOT that returns parrots with Go
Types of preprocessing in natural language processing and their power
I made a quick feed reader using feedparser in Python
I made a simple book application with python + Flask ~ Introduction ~
I made a Numer0n battle game in Java (I also made AI)
I made a tool to create a word cloud from wikipedia