private void ThreadedClassifier() { string[] arguments = Environment.GetCommandLineArgs(); string[] files = Directory.GetFiles(arguments[1], "*.*", SearchOption.AllDirectories); if (files.Count() == 0) { progressBar1.Step = 100; progressBar1.PerformStep(); return; } progressBar1.Step = 100 / files.Count(); foreach (string file in files) { Bitmap streetviewImage = (Bitmap)Image.FromFile(file); StreetviewImageProcessor processor = new StreetviewImageProcessor(); int selectedQuality = 4; StraightLineBias selectedBias = StraightLineBias.MIDDLE; List <GroundInfo> groundPositions = processor.GuessGroundPositions(streetviewImage, (selectedQuality * 5) + 15, true, (StraightLineBias)selectedBias); int groundY = (int)groundPositions[0].position.y; //We only have [0] and [1] when using straight line cutting, both have the same Y Vector2 sunPos = processor.GuessSunPosition(streetviewImage, groundY); streetviewImage = processor.ShiftImageLeft(streetviewImage, (int)sunPos.x - (streetviewImage.Width / 4)); Bitmap classifierOverlay = new Bitmap(streetviewImage.Width, groundY); float avgBlue = 0.0f; float avgRed = 0.0f; float avgGreen = 0.0f; float avgBright = 0.0f; float avgRBDiv = 0.0f; int divMod = 0; for (int x = 0; x < classifierOverlay.Width; x++) { for (int y = 0; y < classifierOverlay.Height; y++) { Color thisSkyPixel = streetviewImage.GetPixel(x, y); avgBlue += thisSkyPixel.B; avgRed += thisSkyPixel.R; avgGreen += thisSkyPixel.G; avgBright += thisSkyPixel.GetBrightness(); if (thisSkyPixel.B == 0) { continue; } avgRBDiv += (float)thisSkyPixel.R / (float)thisSkyPixel.B; divMod++; } } avgBlue /= (float)(classifierOverlay.Width * classifierOverlay.Height); avgRed /= (float)(classifierOverlay.Width * classifierOverlay.Height); avgGreen /= (float)(classifierOverlay.Width * classifierOverlay.Height); avgBright /= (float)(classifierOverlay.Width * classifierOverlay.Height); avgRBDiv /= (float)(divMod); for (int x = 0; x < classifierOverlay.Width; x++) { for (int y = 0; y < classifierOverlay.Height; y++) { Color thisSkyPixel = streetviewImage.GetPixel(x, y); float redBlueDiv = 0.0f; if (thisSkyPixel.B != 0) { redBlueDiv = (float)thisSkyPixel.R / (float)thisSkyPixel.B; } bool check1 = thisSkyPixel.R > avgRed && thisSkyPixel.G > avgGreen && thisSkyPixel.B > avgBlue; bool check2 = (redBlueDiv > (avgRBDiv + (avgRBDiv / 6.5f))); bool check3 = thisSkyPixel.B > thisSkyPixel.G && thisSkyPixel.B > thisSkyPixel.R; if (check2 || (check1 && !check3)) { classifierOverlay.SetPixel(x, y, Color.White); } else { classifierOverlay.SetPixel(x, y, Color.Black); } } } classifierOverlay.Save(file + ".classified.prefill.png", ImageFormat.Png); FloodFill(classifierOverlay, classifierOverlay.Width / 4, (int)sunPos.y, Color.Black); classifierOverlay.Save(file + ".classified.png", ImageFormat.Png); Bitmap streetviewImageTrim = new Bitmap(streetviewImage.Width, groundY); for (int x = 0; x < streetviewImageTrim.Width; x++) { for (int y = 0; y < streetviewImageTrim.Height; y++) { streetviewImageTrim.SetPixel(x, y, streetviewImage.GetPixel(x, y)); } } streetviewImageTrim.Save(file + ".sky.jpg", ImageFormat.Jpeg); } progressBar1.Maximum = progressBar1.Value; }
/* Try and guess ground positions across an image */ public List <GroundInfo> GuessGroundPositions(Image sphere, int acc, bool straight, StraightLineBias bias) { //Work out the classifier between ground and sky float skyClassifier = TakeAverageBrightness(sphere.Height / 6, (Bitmap)sphere); float groundClassifier = TakeAverageBrightness(sphere.Height - (sphere.Height / 6), (Bitmap)sphere); float diffClassifier = skyClassifier - groundClassifier; //First pass of ground position guess, check every column List <GroundInfo> positions = new List <GroundInfo>(); int posOffset = 0; for (int i = 0; i < sphere.Width; i++) { GroundInfo thisGround = new GroundInfo(); thisGround.position = GuessGroundPositionForX(posOffset, (Bitmap)sphere, diffClassifier); thisGround.block_width = 1; positions.Add(thisGround); posOffset += thisGround.block_width; } positions = positions.OrderBy(o => o.position.x).ToList(); //Discredit any outliers on first pass List <GroundInfo> positions_new = new List <GroundInfo>(); int prevDiscredit = 0; int checkRadius = 1; for (int i = checkRadius; i < positions.Count - checkRadius; i++) { if (positions_new.Count != 0) { float prevDif = positions[i].position.y - positions[i - checkRadius].position.y; if (prevDif < 0) { prevDif *= -1; } float nextDif = positions[i].position.y - positions[i + checkRadius].position.y; if (nextDif < 0) { nextDif *= -1; } if (prevDif >= 50 || nextDif >= 50) //todo dont use a set value here, do it by sphere height { prevDiscredit++; continue; } positions_new[positions_new.Count - 1].block_width += prevDiscredit; } positions_new.Add(positions[i]); prevDiscredit = 0; } positions = positions_new; //Second/third pass, get average of blocks from first pass, remove any outliers again, recalculate average positions_new = new List <GroundInfo>(); for (int i = 1; i < positions.Count / acc; i++) { int startPos = acc * (i - 1); int endPos = acc * (i); List <float> yPos = new List <float>(); float avgY = 0.0f; float avgX = 0.0f; for (int x = startPos; x < endPos; x++) { yPos.Add(positions[x].position.y); avgY += positions[x].position.y; avgX += positions[x].position.x; } avgY /= acc; avgX /= acc; float avgY2 = 0.0f; int avgCount = 0; for (int x = 0; x < yPos.Count; x++) { if (yPos[x] >= avgY) { avgY2 += yPos[x]; avgCount++; } } avgY2 /= avgCount; GroundInfo newGndInf = new GroundInfo(); newGndInf.block_width = acc; newGndInf.position = new Vector2(avgX, avgY2); positions_new.Add(newGndInf); } positions = positions_new; //If requested to trim as a straight line, take an average of all points, or pick top/bottom if (straight) { float avgY = 0.0f; switch (bias) { case StraightLineBias.TOP: avgY = float.MaxValue; for (int i = 0; i < positions.Count; i++) { if (avgY >= positions[i].position.y) { avgY = positions[i].position.y; } } break; case StraightLineBias.MIDDLE: for (int i = 0; i < positions.Count; i++) { avgY += positions[i].position.y; } avgY /= positions.Count; break; case StraightLineBias.BOTTOM: for (int i = 0; i < positions.Count; i++) { if (avgY <= positions[i].position.y) { avgY = positions[i].position.y; } } break; } positions.Clear(); GroundInfo avgPos = new GroundInfo(); avgPos.block_width = sphere.Width / 2; avgPos.position = new Vector2(0.0f, avgY); positions.Add(avgPos); avgPos.position = new Vector2(sphere.Width / 2, avgY); positions.Add(avgPos); } return(positions); }