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.
First of all, I briefly checked how Go standard image package works.
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.
--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 **.
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.
--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
--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.
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.
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
}
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.
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).
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.
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
-** 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.
I've derailed, but let's get back to the main file upload process. The processing flow is roughly 3 steps.
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.
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.