package main import ( "os" "image" "log" "image/png" "image/color" "math" "time" ) //var filename = "JPSS1_VIIRS_I01_2017-12-20T07:54:34-02:00.png" var filename = "JPSS1_VIIRS_I01_2017-12-20T08:39:35-02:00.png" type Setting struct { id int name string width int params [6]int params2 [6]int bits int } type BowTie struct { x0 int y0 int x1 int y1 int } var settings = map[string]Setting { "I01": { id: 818, name: "I01", width: 6400, params: [6]int{1280, 736, 1184, 1184, 736, 1280}, params2: [6]int{6, 2, 0, 0, 2, 6}, bits: 31, }, } func IsBlack(r,g,b uint32) bool { return r == 0 && g == 0 && b == 0 } func BilinearInterp(x, y float32, q0, q1, q2, q3 uint32) uint16 { rx := int(math.Floor(float64(x))) ry := int(math.Floor(float64(y))) fracX := float64(x) - float64(rx) fracY := float64(y) - float64(ry) invFracX := 1 - fracX invFracY := 1 - fracY q0f := float64(q0) q1f := float64(q1) q2f := float64(q2) q3f := float64(q3) val := (q0f * invFracX + q1f * fracX + q2f * invFracY + q3f * fracY) / 2 return uint16(val) } func LinearInterp(r float32, q0, q1 uint32) uint16 { return uint16(float32(q0) * r + float32(q1) * (1 - r)) } func CosineInterp(r float32, q0, q1 uint32) uint16 { var mu = float64(r) var mu2 = (1 - math.Cos(mu * math.Pi)) / 2 return uint16(float64(q0) * (1 - mu2) + float64(q1) * (mu2)) } var numPasses = 4 func ProcessBowTie(m image.Image, bt BowTie) { var gm = m.(*image.Gray16) var h = float32(bt.y1 - bt.y0) for x := bt.x0; x < bt.x1; x++ { var q0, _, _, _ = m.At(x, bt.y0 - 1).RGBA() var q1, _, _, _ = m.At(x, bt.y1 + 1).RGBA() for y := bt.y0; y < bt.y1; y++ { // Linear / Cosine var r = float32(y - bt.y0) / float32(h) gm.Set(x, y, color.Gray16{ Y: LinearInterp(r, q0, q1) }) } } for i := 0; i < numPasses; i++ { for x := bt.x0; x < bt.x1; x++ { for y := bt.y0; y < bt.y1; y++ { var q2, _, _, _= m.At(x, y).RGBA() var q3, _, _, _= m.At(x+1, y).RGBA() gm.Set(x, y, color.Gray16{Y: LinearInterp(0.5, q2, q3)}) } } } } func main() { reader, err := os.Open(filename) if err != nil { panic(err) } m, format, err := image.Decode(reader) if err != nil { panic(err) } log.Println("Found image format", format) reader.Close() var currSetting = settings["I01"] var bowTies []BowTie bounds := m.Bounds() startTime := time.Now() // Find head bowtie var x = bounds.Min.X for z := 0; z < 6; z++ { if z == 2 || z == 3 { x += currSetting.params[z] continue } var csw = currSetting.params[z] var csh = currSetting.params2[z] for y := bounds.Min.Y; y < bounds.Max.Y; y++ { r, g, b, _ := m.At(x, y).RGBA() if IsBlack(r, g, b) { //log.Println("Found black pixel at", y) var nextY = y + csh if nextY > bounds.Max.Y || nextY + 1 > bounds.Max.Y { break } nr, ng, nb, _ := m.At(x, nextY).RGBA() n2r, n2g, n2b, _ := m.At(x, nextY + 1).RGBA() if IsBlack(nr, ng, nb) && !IsBlack(n2r, n2g, n2b) { bt := BowTie{ x0: x, y0: y, x1: x + csw + 1, y1: y + csh + 1, } bowTies = append(bowTies, bt) y += currSetting.params2[0] } } } x += currSetting.params[z] } // Fill Bow Ties with red for i := 0; i < len(bowTies); i++ { //log.Println("Paiting bowtie", i) ProcessBowTie(m, bowTies[i]) } delta := time.Now().Sub(startTime).Seconds() log.Println("Took", delta, "seconds to debowtie!") out, err := os.Create("out.png") err = png.Encode(out, m) if err != nil { panic(err) } out.Close() }