diff --git a/patches/improve-image-quality/improve-image-quality.go b/patches/improve-image-quality/improve-image-quality.go index a5d68c24..c2ae268a 100644 --- a/patches/improve-image-quality/improve-image-quality.go +++ b/patches/improve-image-quality/improve-image-quality.go @@ -1,287 +1,289 @@ package main -import ( - "bytes" - "fmt" - "image" - "image/color" - _ "image/jpeg" - "image/png" - "io/ioutil" - "os" -) +func main() {} -func main() { - data, err := ioutil.ReadFile("input.jpg") +// import ( +// "bytes" +// "fmt" +// "image" +// "image/color" +// _ "image/jpeg" +// "image/png" +// "io/ioutil" +// "os" +// ) - if err != nil { - panic(err) - } +// func main() { +// data, err := ioutil.ReadFile("input.jpg") - img, _, err := image.Decode(bytes.NewReader(data)) +// if err != nil { +// panic(err) +// } - if err != nil { - panic(err) - } +// img, _, err := image.Decode(bytes.NewReader(data)) - improved := ImproveQuality(img) - f, err := os.Create("output.png") +// if err != nil { +// panic(err) +// } - if err != nil { - panic(err) - } +// improved := ImproveQuality(img) +// f, err := os.Create("output.png") - defer f.Close() - png.Encode(f, improved) -} +// if err != nil { +// panic(err) +// } -// Pixel ... -type Pixel struct { - X int - Y int -} +// defer f.Close() +// png.Encode(f, improved) +// } -// Area ... -type Area struct { - Pixels []Pixel - totalR uint64 - totalG uint64 - totalB uint64 - totalA uint64 -} +// // Pixel ... +// type Pixel struct { +// X int +// Y int +// } -// Add ... -func (area *Area) Add(x, y int, r, g, b, a uint32) { - area.Pixels = append(area.Pixels, Pixel{ - X: x, - Y: y, - }) +// // Area ... +// type Area struct { +// Pixels []Pixel +// totalR uint64 +// totalG uint64 +// totalB uint64 +// totalA uint64 +// } - area.totalR += uint64(r) - area.totalG += uint64(g) - area.totalB += uint64(b) - area.totalA += uint64(a) -} +// // Add ... +// func (area *Area) Add(x, y int, r, g, b, a uint32) { +// area.Pixels = append(area.Pixels, Pixel{ +// X: x, +// Y: y, +// }) -// AverageColor ... -func (area *Area) AverageColor() color.Color { - if len(area.Pixels) == 0 { - return color.Transparent - } +// area.totalR += uint64(r) +// area.totalG += uint64(g) +// area.totalB += uint64(b) +// area.totalA += uint64(a) +// } - return color.RGBA64{ - R: uint16(area.totalR / uint64(len(area.Pixels))), - G: uint16(area.totalG / uint64(len(area.Pixels))), - B: uint16(area.totalB / uint64(len(area.Pixels))), - A: uint16(area.totalA / uint64(len(area.Pixels))), - } -} +// // AverageColor ... +// func (area *Area) AverageColor() color.Color { +// if len(area.Pixels) == 0 { +// return color.Transparent +// } -const ( - tolerance = uint32(3000) -) +// return color.RGBA64{ +// R: uint16(area.totalR / uint64(len(area.Pixels))), +// G: uint16(area.totalG / uint64(len(area.Pixels))), +// B: uint16(area.totalB / uint64(len(area.Pixels))), +// A: uint16(area.totalA / uint64(len(area.Pixels))), +// } +// } -func diffAbs(a uint32, b uint32) uint32 { - if a > b { - return a - b - } +// const ( +// tolerance = uint32(3000) +// ) - return b - a -} +// func diffAbs(a uint32, b uint32) uint32 { +// if a > b { +// return a - b +// } -// ImproveQuality returns the average color of an image in HSL format. -func ImproveQuality(img image.Image) *image.NRGBA { - width := img.Bounds().Dx() - height := img.Bounds().Dy() - clone := image.NewNRGBA(image.Rect(0, 0, width, height)) - areas := []*Area{} - areaIndexMap := make([]int, width*height) +// return b - a +// } - for x := 0; x < width; x++ { - for y := 0; y < height; y++ { - color := img.At(x, y) - r, g, b, a := color.RGBA() - areaIndex := -1 +// // ImproveQuality returns the average color of an image in HSL format. +// func ImproveQuality(img image.Image) *image.NRGBA { +// width := img.Bounds().Dx() +// height := img.Bounds().Dy() +// clone := image.NewNRGBA(image.Rect(0, 0, width, height)) +// areas := []*Area{} +// areaIndexMap := make([]int, width*height) - // Find similar area - for i := 0; i < len(areas); i++ { - area := areas[i] - avgR, avgG, avgB, _ := area.AverageColor().RGBA() +// for x := 0; x < width; x++ { +// for y := 0; y < height; y++ { +// color := img.At(x, y) +// r, g, b, a := color.RGBA() +// areaIndex := -1 - // Is the color similar? - if diffAbs(r, avgR) <= tolerance && diffAbs(g, avgG) <= tolerance && diffAbs(b, avgB) <= tolerance { - areaIndex = i - break - } - } +// // Find similar area +// for i := 0; i < len(areas); i++ { +// area := areas[i] +// avgR, avgG, avgB, _ := area.AverageColor().RGBA() - // Insert new area - if areaIndex == -1 { - areaIndex = len(areas) - areas = append(areas, &Area{}) - } +// // Is the color similar? +// if diffAbs(r, avgR) <= tolerance && diffAbs(g, avgG) <= tolerance && diffAbs(b, avgB) <= tolerance { +// areaIndex = i +// break +// } +// } - areaIndexMap[y*width+x] = areaIndex - areas[areaIndex].Add(x, y, r, g, b, a) - } - } +// // Insert new area +// if areaIndex == -1 { +// areaIndex = len(areas) +// areas = append(areas, &Area{}) +// } - fmt.Println(len(areas), "areas") +// areaIndexMap[y*width+x] = areaIndex +// areas[areaIndex].Add(x, y, r, g, b, a) +// } +// } - // Reduce noise - noiseCount := 0 +// fmt.Println(len(areas), "areas") - for areaIndex, area := range areas { - noisePixelIndices := []int{} - areaSurroundedBy := map[int]int{} +// // Reduce noise +// noiseCount := 0 - for i := 0; i < len(area.Pixels); i++ { - // If pixel is surrounded by 4 different areas, remove it - pixel := area.Pixels[i] - x := pixel.X - y := pixel.Y - left := areaIndex - right := areaIndex - top := areaIndex - bottom := areaIndex +// for areaIndex, area := range areas { +// noisePixelIndices := []int{} +// areaSurroundedBy := map[int]int{} - if x > 0 { - left = areaIndexMap[y*width+(x-1)] - } +// for i := 0; i < len(area.Pixels); i++ { +// // If pixel is surrounded by 4 different areas, remove it +// pixel := area.Pixels[i] +// x := pixel.X +// y := pixel.Y +// left := areaIndex +// right := areaIndex +// top := areaIndex +// bottom := areaIndex - if x < width-1 { - right = areaIndexMap[y*width+(x+1)] - } +// if x > 0 { +// left = areaIndexMap[y*width+(x-1)] +// } - if y > 0 { - top = areaIndexMap[(y-1)*width+x] - } +// if x < width-1 { +// right = areaIndexMap[y*width+(x+1)] +// } - if y < height-1 { - bottom = areaIndexMap[(y+1)*width+x] - } +// if y > 0 { +// top = areaIndexMap[(y-1)*width+x] +// } - differentNeighbors := 0 +// if y < height-1 { +// bottom = areaIndexMap[(y+1)*width+x] +// } - if left != areaIndex { - differentNeighbors++ - } +// differentNeighbors := 0 - if right != areaIndex { - differentNeighbors++ - } +// if left != areaIndex { +// differentNeighbors++ +// } - if top != areaIndex { - differentNeighbors++ - } +// if right != areaIndex { +// differentNeighbors++ +// } - if bottom != areaIndex { - differentNeighbors++ - } +// if top != areaIndex { +// differentNeighbors++ +// } - // Determine surrounding area - areaIndexScore := map[int]int{} +// if bottom != areaIndex { +// differentNeighbors++ +// } - areaIndexScore[left]++ - areaIndexScore[right]++ - areaIndexScore[top]++ - areaIndexScore[bottom]++ +// // Determine surrounding area +// areaIndexScore := map[int]int{} - areaSurroundedBy[left]++ - areaSurroundedBy[right]++ - areaSurroundedBy[top]++ - areaSurroundedBy[bottom]++ +// areaIndexScore[left]++ +// areaIndexScore[right]++ +// areaIndexScore[top]++ +// areaIndexScore[bottom]++ - newAreaIndex := -1 - bestScore := 0 +// areaSurroundedBy[left]++ +// areaSurroundedBy[right]++ +// areaSurroundedBy[top]++ +// areaSurroundedBy[bottom]++ - for checkIndex, score := range areaIndexScore { - if score > bestScore { - bestScore = score - newAreaIndex = checkIndex - } - } +// newAreaIndex := -1 +// bestScore := 0 - if differentNeighbors >= 3 && bestScore >= 3 { - noiseCount++ - noisePixelIndices = append(noisePixelIndices, i) +// for checkIndex, score := range areaIndexScore { +// if score > bestScore { +// bestScore = score +// newAreaIndex = checkIndex +// } +// } - // Add to surrounding area - r, g, b, a := img.At(x, y).RGBA() - areas[newAreaIndex].Add(x, y, r, g, b, a) +// if differentNeighbors >= 3 && bestScore >= 3 { +// noiseCount++ +// noisePixelIndices = append(noisePixelIndices, i) - area.totalR -= uint64(r) - area.totalG -= uint64(g) - area.totalB -= uint64(b) - area.totalA -= uint64(a) - } - } +// // Add to surrounding area +// r, g, b, a := img.At(x, y).RGBA() +// areas[newAreaIndex].Add(x, y, r, g, b, a) - // Remove noise pixels - offset := 0 +// area.totalR -= uint64(r) +// area.totalG -= uint64(g) +// area.totalB -= uint64(b) +// area.totalA -= uint64(a) +// } +// } - for _, removal := range noisePixelIndices { - index := removal - offset - area.Pixels = append(area.Pixels[:index], area.Pixels[index+1:]...) - offset++ - } +// // Remove noise pixels +// offset := 0 - // // Determine surrounding area - // surroundingAreaIndex := -1 - // bestScore := 0 +// for _, removal := range noisePixelIndices { +// index := removal - offset +// area.Pixels = append(area.Pixels[:index], area.Pixels[index+1:]...) +// offset++ +// } - // for checkIndex, score := range areaSurroundedBy { - // if score > bestScore && checkIndex != areaIndex { - // bestScore = score - // surroundingAreaIndex = checkIndex - // } - // } +// // // Determine surrounding area +// // surroundingAreaIndex := -1 +// // bestScore := 0 - // surroundingArea := areas[surroundingAreaIndex] +// // for checkIndex, score := range areaSurroundedBy { +// // if score > bestScore && checkIndex != areaIndex { +// // bestScore = score +// // surroundingAreaIndex = checkIndex +// // } +// // } - // if areaIndex != surroundingAreaIndex && len(surroundingArea.Pixels) > len(area.Pixels)*2 { - // // const surroundTolerance = 5000 +// // surroundingArea := areas[surroundingAreaIndex] - // // r1, g1, b1, a1 := area.AverageColor().RGBA() - // // r2, g2, b2, a2 := surroundingArea.AverageColor().RGBA() +// // if areaIndex != surroundingAreaIndex && len(surroundingArea.Pixels) > len(area.Pixels)*2 { +// // // const surroundTolerance = 5000 - // // if diffAbs(r1, r2) < surroundTolerance && diffAbs(g1, g2) < surroundTolerance && diffAbs(b1, b2) < surroundTolerance && diffAbs(a1, a2) < surroundTolerance { - // // // fmt.Println(areaIndex, "surrounded by", surroundingAreaIndex, "|", len(area.Pixels), len(surroundingArea.Pixels)) +// // // r1, g1, b1, a1 := area.AverageColor().RGBA() +// // // r2, g2, b2, a2 := surroundingArea.AverageColor().RGBA() - // // // Add pixels to surrounding area - // // for _, pixel := range area.Pixels { - // // r, g, b, a := img.At(pixel.X, pixel.Y).RGBA() - // // surroundingArea.Add(pixel.X, pixel.Y, r, g, b, a) - // // } +// // // if diffAbs(r1, r2) < surroundTolerance && diffAbs(g1, g2) < surroundTolerance && diffAbs(b1, b2) < surroundTolerance && diffAbs(a1, a2) < surroundTolerance { +// // // // fmt.Println(areaIndex, "surrounded by", surroundingAreaIndex, "|", len(area.Pixels), len(surroundingArea.Pixels)) - // // // Remove this area - // // area.Pixels = nil - // // area.totalR = 0 - // // area.totalG = 0 - // // area.totalB = 0 - // // area.totalA = 0 - // // } - // } - } +// // // // Add pixels to surrounding area +// // // for _, pixel := range area.Pixels { +// // // r, g, b, a := img.At(pixel.X, pixel.Y).RGBA() +// // // surroundingArea.Add(pixel.X, pixel.Y, r, g, b, a) +// // // } - fmt.Println(noiseCount, "noise pixels") +// // // // Remove this area +// // // area.Pixels = nil +// // // area.totalR = 0 +// // // area.totalG = 0 +// // // area.totalB = 0 +// // // area.totalA = 0 +// // // } +// // } +// } - pixelCount := 0 +// fmt.Println(noiseCount, "noise pixels") - for _, area := range areas { - pixelCount += len(area.Pixels) - } +// pixelCount := 0 - fmt.Println(pixelCount, "pixels", width*height) +// for _, area := range areas { +// pixelCount += len(area.Pixels) +// } - // Build image from areas - for _, area := range areas { - avgColor := area.AverageColor() +// fmt.Println(pixelCount, "pixels", width*height) - for _, pixel := range area.Pixels { - clone.Set(pixel.X, pixel.Y, avgColor) - } - } +// // Build image from areas +// for _, area := range areas { +// avgColor := area.AverageColor() - return clone -} +// for _, pixel := range area.Pixels { +// clone.Set(pixel.X, pixel.Y, avgColor) +// } +// } + +// return clone +// }