Created
April 2, 2015 20:47
-
-
Save leestuartx/b808fd855bdea8f09d9c to your computer and use it in GitHub Desktop.
Image Tracking in Unity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine.UI; | |
public enum ImageDetectAlgorithm | |
{ | |
corner, | |
fill, | |
blobs, //corner detect, edge detect and Fill | |
} | |
public class BlobData | |
{ | |
public string name = "new object"; | |
public List<Vector2> edgesFound; //List of all the edges for the current tracked building | |
public List<Vector2> cornersFound; | |
} | |
public class PixelRecognition : MonoBehaviour | |
{ | |
public GUITexture videoFootage; //Main texture for tracking | |
public GameObject ScannedImageObject; | |
Color32[] imageColors; //Local copy of texture's pixel values | |
public ImageDetectAlgorithm curDetectionScheme = ImageDetectAlgorithm.fill; | |
//------------------------------------------------- | |
//Details about each tracked object | |
//------------------------------------------------- | |
List<BlobData> blobData = new List<BlobData>(); | |
private bool hasFoundNewObject; // Used to advance the program to check for an object's edges | |
private List<Vector2> edgesFound = new List<Vector2>(); //List of all the edges for the current tracked building | |
private List<Vector2> cornersFound = new List<Vector2>(); | |
//--------------------------------- | |
// DEBUG GUI | |
//--------------------------------- | |
public Text statusLabel; //Displays progress messages for the tracking procedure | |
public Text blobDetailLabel; //Displays the stats of each detected object on screen | |
private string accumStatus = "Status: "; | |
private int charCount = 0; | |
private float searchDistance = 3f; //Pixel Polygon detection distance (3 seems to be the best setting) | |
private float cornerMinRadius = 0.54f; //Used to determine min angle allowed for a corner to be detected | |
//Steps required to track an object | |
private bool step1 = true; | |
private bool step2 = false; | |
private bool step3 = false; | |
private bool step4 = false; | |
//The current surrounding pixel to be tracked | |
SearchDirection curDirection = SearchDirection.right; | |
SearchDirection lastDirection = SearchDirection.right; | |
//Vars used for keeping track of surrounding pixels | |
private bool mark1; | |
private bool mark2; | |
private bool mark3; | |
private bool mark4; | |
private bool mark5; | |
private bool mark6; | |
private bool mark7; | |
private bool mark8; | |
private bool mark9; | |
int lastSelectedPos = 3; //The pixel position on a grid of 9 pixels, which is the current pixel and the surrounding 8 pixels | |
//If there are more identical colors on the top then we will start detecting direction from the bottom, otherwise we will start detecting from the top | |
int upperHalfCount = 0; | |
int lowerHalfCount = 0; | |
//------------------------------------------------- | |
//Colors used for searching for pixels | |
//------------------------------------------------- | |
public int errorCorrect = 20; | |
public int matchingColorErrorCorrect = 2; | |
public Color32 buildingColor = new Color32(244, 243, 236, 1); | |
Color32 EdgeColor = new Color32(0, 0, 255, 1); | |
Color Black = new Color(0f, 0f, 0f, 1.0f); | |
Color Green = new Color(0f, 1f, 0f, 1.0f); | |
Color Red = new Color(1f, 0f, 0f, 1.0f); | |
public bool debugDrawProgress = true; | |
public int pixelCancelationValue = 4; //The difference between pixels colors to not count as the same building | |
int totalErrors = 0; | |
int matchingColorCount = 0; | |
private bool hasStartedEdgeDetect; | |
private bool hasSetNextDirection; | |
private bool hasFinishedCheckingDirection; | |
private bool doCornerCheckRoutine = true; | |
private bool hasStartedProcess = false; | |
//-------------------------------- | |
// GOOGLE MAPS VARS | |
//-------------------------------- | |
//Used for keeping accurate latitude and longitude from tracking a google map | |
private int zoomLevel; | |
public int mapSize; | |
void Start() | |
{ | |
// DoRecognition(); | |
} | |
int CoordToPixelArray(Vector2 coords) | |
{ | |
//SetStatus("coordX:" + coords.x + " coordY:" + coords.y + " texWidth:" + videoFootage.texture.width); | |
int index = (int)(coords.x + coords.y * videoFootage.texture.width); | |
return index; | |
} | |
Vector2 arrayToXYCoord(int curIndex) | |
{ | |
int width = (int)videoFootage.texture.width; | |
int curY = curIndex / width; | |
int curX = curIndex % width; | |
return new Vector2(curX, curY); | |
} | |
public bool HasStartedTracking() | |
{ | |
return hasStartedProcess; | |
} | |
public GameObject GetTrackingImage() | |
{ | |
return ScannedImageObject; | |
} | |
//Adds the tracked values to building edge array | |
void AddToBuildingEdge(Vector2 coords) | |
{ | |
edgesFound.Add(coords); | |
} | |
//Sets the pixel color stored in the multidimensional array for the tracked image | |
void SetPixelFillColor(Vector2 coord, Color32 newColor){ | |
if (coord.x >= 0 && coord.x <= videoFootage.texture.width && coord.y >= 0 && coord.y <= videoFootage.texture.height) | |
{ | |
//Store the pixel locally | |
imageColors[CoordToPixelArray(coord)] = newColor; | |
} | |
} | |
void SetPixelEdgeColor(Vector2 coord, Color32 newColor) | |
{ | |
if (coord.x >= 0 && coord.x <= videoFootage.texture.width && coord.y >= 0 && coord.y <= videoFootage.texture.height) | |
{ | |
//Store the pixel locally | |
imageColors[CoordToPixelArray(coord)] = newColor; | |
edgesFound.Add(coord); | |
if (debugDrawProgress) | |
{ | |
RecolorImage(); | |
} | |
} | |
} | |
//Used as a marker to block out portions of the image | |
void SetPixelMarkerColor(Vector2 coord, Color32 newColor) | |
{ | |
if (coord.x > 0 && coord.x < videoFootage.texture.width && coord.y > 0 && coord.y < videoFootage.texture.height) | |
{ | |
//Store the pixel locally | |
imageColors[CoordToPixelArray(coord)] = newColor; | |
} | |
} | |
//Gets the pixel color stored in the multidimensional array for the tracked image | |
Color32 GetPixelColor(Vector2 coord) | |
{ | |
if (coord.x > 0 && coord.x < videoFootage.texture.width && coord.y > 0 && coord.y < videoFootage.texture.height) | |
{ | |
return imageColors[CoordToPixelArray(coord)]; | |
} | |
else{ | |
//SetStatus("Off Screen at: " + coord); | |
return new Color32(0, 0, 0, 1); //This pixel is off the screen | |
} | |
} | |
public void DoRecognition() | |
{ | |
//SetScale(); | |
//GetAllColoredPixels(); | |
if (!hasStartedProcess) | |
{ | |
//Get the zoom level for google maps | |
//GoogleMap gMap = gameObject.GetComponent<GoogleMap>(); | |
// if (gMap) | |
// { | |
zoomLevel = 19;// gMap.GetZoom(); | |
mapSize = 512;// gMap.GetSize(); | |
// } | |
hasStartedProcess = true; | |
SetStatus("Doing Recognition"); | |
GetPoints(); | |
} | |
} | |
void SetStatus(string newStatus) | |
{ | |
//Make sure enough characters can be used per line | |
// char[] totalChars = newStatus.ToCharArray(); | |
// charCount += totalChars.Length; | |
//accumStatus += "2"; | |
accumStatus += newStatus + " | ";//\n"; | |
// accumStatus = newStatus; | |
statusLabel.text = accumStatus;// "Status: " + newStatus + "."; | |
} | |
void AddBlob() | |
{ | |
BlobData newBlob = new BlobData(); | |
newBlob.name = "new object " + (blobData.Count + 1); | |
newBlob.edgesFound = new List<Vector2>(edgesFound); //List of all the edges for the current tracked building | |
newBlob.cornersFound = new List<Vector2>(cornersFound); | |
blobData.Add(newBlob); | |
UpdateBlobDetails(); | |
} | |
public List<Vector2> GetFirstBlobCorners() | |
{ | |
if (blobData.Count > 0) | |
return blobData[0].cornersFound; | |
else | |
return new List<Vector2>(); | |
} | |
//Grabs a list of all the corners for every tracked object | |
public List<TracedBuildingType> GetAllObjectCorners(float scaleFactor) | |
{ | |
List<TracedBuildingType> allBuildings = new List<TracedBuildingType>(); | |
for (int i = 0; i < blobData.Count; i++) | |
{ | |
TracedBuildingType newBuilding = new TracedBuildingType(); | |
newBuilding.name = blobData[i].name; | |
List<Vector2z> cornerPoints = new List<Vector2z>(); | |
for (int k = 0; k < blobData[i].cornersFound.Count; k++) | |
{ | |
cornerPoints.Add(new Vector2z(blobData[i].cornersFound[k].x * scaleFactor, blobData[i].cornersFound[k].y * scaleFactor)); | |
} | |
newBuilding.cornerPoints = cornerPoints; | |
allBuildings.Add(newBuilding); | |
} | |
Debug.Log("Finished getting object corners"); | |
return allBuildings; | |
} | |
void UpdateBlobDetails() | |
{ | |
string curBlobStats = "Object Count:" + blobData.Count + "\n"; | |
for (int i = 0; i < blobData.Count; i++) | |
{ | |
curBlobStats += blobData[i].name + ", Corners:" + blobData[i].cornersFound.Count + "\n"; | |
} | |
blobDetailLabel.text = curBlobStats; | |
} | |
void SetTrackingAsComplete() | |
{ | |
string blobDetails = blobDetailLabel.text; | |
blobDetails += "\n Completed!! \n Now run mapity and transform the tracked images into 3D buildings."; | |
blobDetailLabel.text = blobDetails; | |
//-------------------------------------------------------------- | |
//Debug all the building data | |
//-------------------------------------------------------------- | |
for (int i = 0; i < blobData.Count; i++) | |
{ | |
string curBuildingValue = blobData[i].name + ": "; | |
for (int k = 0; k < blobData[i].cornersFound.Count; k++){ | |
curBuildingValue += blobData[i].cornersFound[k].ToString() + ","; | |
} | |
Debug.Log(curBuildingValue); | |
} | |
hasStartedProcess = false; | |
} | |
void GetPoints() | |
{ | |
SetStatus("1. Getting Points"); | |
step1 = true; | |
StartCoroutine(WaitBeforeStart()); | |
} | |
void ClearArrays() | |
{ | |
//--------------------------------------------- | |
// Reset the temporary texture to work with | |
//--------------------------------------------- | |
imageColors = new Color32[0]; | |
Texture2D curTexture = videoFootage.texture as Texture2D; | |
imageColors = curTexture.GetPixels32(); | |
SetStatus("Total Pixels: " + imageColors.Length); | |
//Clear out the arrays of stored vertices per building | |
cornersFound.Clear(); | |
edgesFound.Clear(); | |
hasStartedEdgeDetect = false; | |
doCornerCheckRoutine = true; | |
hasFoundNewObject = false; | |
} | |
//Process for searching image for pixels | |
IEnumerator WaitBeforeStart() | |
{ | |
while (true) | |
{ | |
if (step1) | |
{ | |
step1 = false; | |
ClearArrays(); | |
SetStatus("Locating the blob edges"); | |
LocateBlobEdges(); | |
} | |
if (step2) | |
{ | |
step2 = false; | |
SetStatus("Step 2 activated"); | |
SearchNextPixel(cornersFound[0]); | |
//StartCoroutine(SearchNextPixel(coordsFound[0])); | |
} | |
/* | |
if (step3) //should be step 3 | |
{ | |
step3 = false; | |
//Color the surrounding pixels to identify the pixel | |
for (int i = 0; i < coordsFound.Count; i++) | |
{ | |
StartCoroutine(ColorSurroundingPixels(coordsFound[i], 4)); | |
} | |
}*/ | |
if (step4) | |
{ | |
step4 = false; | |
SetStatus("Filling Polygon"); | |
StartCoroutine(FillPolygon()); | |
} | |
yield return new WaitForSeconds(0.3f); | |
} | |
} | |
/** Getting pixel data in a coroutine causes a freeze, takes way too long, must be in standard function */ | |
void LocateBlobEdges() | |
{ | |
//For every color that isn't black, recolor it with yellow | |
Color32 pixel_color = new Color(0f, 0f, 0f, 1); | |
Color MarkerColor = new Color(1f, .4f, 1f); | |
Texture2D curImage = (Texture2D)videoFootage.texture; | |
SetStatus("Locating the blobs"); | |
//Goes through every pixel and gets their color, checks the color against the tracking color | |
int imageWidth = curImage.width; | |
int imageHeight = curImage.height; | |
SetStatus("Grabbing Pixel colors"); | |
//------------------------------------------------------------ | |
// | |
// Grab all the image pixel colors and put them into an array | |
// | |
//------------------------------------------------------------ | |
//Go through all the pixels in the image | |
for (int curIndex = 0; curIndex < imageColors.Length; curIndex++) | |
//for (int curIndex = 0; curIndex < 100; curIndex++) | |
{ | |
Vector2 currentCoordinate = arrayToXYCoord(curIndex); | |
pixel_color = GetPixelColor(currentCoordinate); | |
if (hasFoundNewObject) | |
return; | |
switch (curDetectionScheme) | |
{ | |
case ImageDetectAlgorithm.corner: | |
// SetStatus("pixel_color:" + pixel_color + " buildingColor:" + buildingColor); | |
if (CompareColors(pixel_color, buildingColor, errorCorrect)) | |
{ | |
CheckMatchingColor(0, currentCoordinate); | |
if (matchingColorCount < 7) //Skip pixels that are in the center of a building that are surrounded by matching pixels | |
{ | |
//Allow the routine to break after this cycle | |
hasFoundNewObject = true; | |
SetStatus("BUILDING FOUND! At: " + currentCoordinate.x + "," + currentCoordinate.y); | |
//Save the current coordinate as a corner point | |
cornersFound.Add(currentCoordinate); | |
//Create a color box to identify the current point as a corner | |
ColorSurroundingPixels(currentCoordinate, 2, EdgeColor); | |
//Activate the corner detection algorithm | |
step2 = true; | |
} | |
} | |
break; | |
case ImageDetectAlgorithm.fill: | |
if (CompareColors(pixel_color, buildingColor, errorCorrect)) | |
{ | |
SetPixelFillColor(currentCoordinate, Red); | |
} | |
break; | |
} | |
} | |
switch(curDetectionScheme){ | |
case ImageDetectAlgorithm.fill: | |
RecolorImage(); | |
break; | |
} | |
SetTrackingAsComplete(); | |
} | |
//Color pixels within a range of pixel distance, will color the pixels surrounding the current pixel | |
void ColorSurroundingPixels(Vector2 basePixel, int pixelDistance) | |
{ | |
Color32 newColor = new Color32(0, 0, 0, 1); | |
for (int i = 0; i < pixelDistance; i++) | |
{ | |
SetPixelMarkerColor(GoRight(basePixel), newColor); | |
SetPixelMarkerColor(GoLeft(basePixel), newColor); | |
SetPixelMarkerColor(GoUp(basePixel), newColor); | |
SetPixelMarkerColor(GoDown(basePixel), newColor); | |
SetPixelMarkerColor(GoUpRight(basePixel), newColor); | |
SetPixelMarkerColor(GoDownRight(basePixel), newColor); | |
SetPixelMarkerColor(GoUpLeft(basePixel), newColor); | |
SetPixelMarkerColor(GoDownLeft(basePixel), newColor); | |
SetPixelMarkerColor(basePixel, newColor); | |
} | |
} | |
void ColorSurroundingPixels(Vector2 basePixel, int pixelDistance, Color32 colorType) | |
{ | |
for (int i = 0; i < pixelDistance; i++) | |
{ | |
SetPixelMarkerColor(GoRight(basePixel), colorType); | |
SetPixelMarkerColor(GoLeft(basePixel), colorType); | |
SetPixelMarkerColor(GoUp(basePixel), colorType); | |
SetPixelMarkerColor(GoDown(basePixel), colorType); | |
SetPixelMarkerColor(GoUpRight(basePixel), colorType); | |
SetPixelMarkerColor(GoDownRight(basePixel), colorType); | |
SetPixelMarkerColor(GoUpLeft(basePixel), colorType); | |
SetPixelMarkerColor(GoDownLeft(basePixel), colorType); | |
SetPixelMarkerColor(basePixel, colorType); | |
} | |
} | |
void ColorPixelsBetween(Vector2 startCoordinate, Vector2 endCoordinate) | |
{ | |
for (int curY = (int)startCoordinate.y; curY < (int)endCoordinate.y; curY++) | |
{ | |
//Fill all the pixels from the start coordinate to the end coordinate | |
SetPixelFillColor(new Vector2(startCoordinate.x, curY), Red); | |
} | |
} | |
IEnumerator FillPolygon() | |
{ | |
SetStatus("Starting the Fill Procedure"); | |
for (int i = 0; i < edgesFound.Count; i++) | |
{ | |
Vector2 startCoordinate = edgesFound[i]; | |
Vector2 endCoordinate = edgesFound[i]; | |
//Need to compare the current pixel coord with each edge pixels and color all pixels between them | |
for (int j = 0; j < edgesFound.Count; j++) | |
{ | |
//Make sure we are on the correct x position | |
if (edgesFound[j].x == startCoordinate.x) | |
{ | |
//Determine if the coordinate is greater or less than the last point and store that | |
if (edgesFound[j].y < startCoordinate.y) | |
startCoordinate.y = edgesFound[j].y; | |
if (edgesFound[j].y > endCoordinate.y) | |
endCoordinate.y = edgesFound[j].y; | |
} | |
} | |
//Color the pixels between the current start and end points on each x position | |
ColorPixelsBetween(startCoordinate, endCoordinate); | |
} | |
//Apply the color to the image and then repeat the steps to process the next shape | |
RecolorImage(); | |
SetStatus("Completed the fill procedure"); | |
//Start the initial loop again.. | |
step1 = true; | |
StopCoroutine("WaitBeforeStart"); | |
//DoRecognition(); | |
yield return 0; | |
} | |
//Shifts the starting check pixel based on the last direction | |
IEnumerator IdentifyCorners(Vector2 nextPixel) | |
{ | |
bool hasStartedStep1 = false; | |
int pixelsDrawn = 0; | |
Vector2 startPos = nextPixel; | |
List<Vector2> checkEdges = new List<Vector2>(); | |
Vector2 edgeDirection = Vector2.zero; | |
Vector2 initialEdgeDirection = Vector2.zero; | |
Vector2 trakPos = nextPixel; | |
Vector2 initHead = Vector2.zero; | |
SetStatus("Corner Routine at "+ nextPixel); | |
while (doCornerCheckRoutine) | |
{ | |
if (!hasStartedStep1) | |
{ | |
hasStartedStep1 = true; | |
int tempNextPos = lastSelectedPos - 2; | |
if (tempNextPos == -1) | |
tempNextPos = 7; | |
else if (tempNextPos == -2) | |
tempNextPos = 6; | |
else if (tempNextPos == -3) | |
tempNextPos = 5; | |
//Here we will determine the next search direction | |
int identicalPixelCount = GetEdgeBasedOnStart(nextPixel, buildingColor, tempNextPos); | |
//The new direction will now be based of the last direction | |
switch (curDirection) | |
{ | |
case SearchDirection.up: nextPixel = GoUp(nextPixel); | |
lastSelectedPos = 0; | |
break; | |
case SearchDirection.upRight: nextPixel = GoUpRight(nextPixel); | |
lastSelectedPos = 1; | |
break; | |
case SearchDirection.right: nextPixel = GoRight(nextPixel); | |
lastSelectedPos = 2; | |
break; | |
case SearchDirection.downRight: nextPixel = GoDownRight(nextPixel); | |
lastSelectedPos = 3; | |
break; | |
case SearchDirection.down: nextPixel = GoDown(nextPixel); | |
lastSelectedPos = 4; | |
break; | |
case SearchDirection.downLeft: nextPixel = GoDownLeft(nextPixel); | |
lastSelectedPos = 5; | |
break; | |
case SearchDirection.left: nextPixel = GoLeft(nextPixel); | |
lastSelectedPos = 6; | |
break; | |
case SearchDirection.upLeft: nextPixel = GoUpLeft(nextPixel); | |
lastSelectedPos = 7; | |
break; | |
} | |
if (debugDrawProgress) | |
{ | |
SetPixelFillColor(nextPixel,new Color32(1,0,1,1)); | |
RecolorImage(); | |
} | |
//Try to close the loop | |
pixelsDrawn++; | |
if (checkEdges.Count < 4) | |
{ | |
//Add the pixel to an array for corner detection | |
checkEdges.Add(nextPixel); | |
Vector2 cPoint = trakPos; | |
Vector2 nPoint = nextPixel; | |
initHead = nPoint - cPoint; | |
initialEdgeDirection = initHead / initHead.magnitude; | |
} | |
else | |
{ | |
//Create point between current and next | |
Vector2 cPoint = trakPos; | |
Vector2 nPoint = nextPixel; | |
//Attributes of the two points | |
Vector2 head = nPoint - cPoint; | |
//Get the distance between the first and second point | |
float dist = head.magnitude; | |
float degR = Vector2.Angle(cPoint, nPoint); | |
edgeDirection = head / dist; | |
float diff = Vector2.Distance(initialEdgeDirection, edgeDirection); | |
//-------------------------------------------------- | |
//Setting corners based on radius of cornerMinRadius | |
//----------------------------------------------------------- | |
if (Mathf.Abs(diff) > cornerMinRadius) //.4 too little, .6 too high | |
{ | |
cornersFound.Add(nextPixel); | |
checkEdges.Clear(); | |
} | |
trakPos = nextPixel; | |
} | |
//-------------------------------------- | |
//Step 2: | |
//Setup edge outline | |
//-------------------------------------- | |
if (identicalPixelCount > 0) | |
{ | |
hasStartedEdgeDetect = true; | |
SetPixelEdgeColor(nextPixel, Green); | |
} | |
if ( totalErrors > 3){ | |
totalErrors = 0; | |
SetStatus("Can't track this object..."); | |
doCornerCheckRoutine = false; //Close the loop | |
//Apply the tracking data to the image for visualization | |
RecolorImage(); | |
step4 = true; //Start filling the polygon | |
yield break; | |
} | |
//--------------------------------------------------------------- | |
// Allow polygon completion check routine to be run | |
// only after 9 pixels have been drawn | |
//--------------------------------------------------------------- | |
if (pixelsDrawn > 9) | |
{ | |
//Attributes of the two points | |
Vector2 heading = nextPixel - startPos; | |
//Get the distance between the first and second point | |
float distance = heading.magnitude; | |
// Debug.Log("distance: " + distance); | |
if (distance < searchDistance) | |
{ | |
SetStatus("Distane is less than " + searchDistance); | |
//Only called after a start and end point has been made, temporary... | |
if (hasStartedEdgeDetect) | |
{ | |
hasStartedEdgeDetect = false; | |
SetStatus("Completed the building"); | |
//Verify Close off the building edges | |
for (int i = 0; i < 3; i++) | |
{ | |
//The new direction will now be based of the last direction | |
switch (curDirection) | |
{ | |
case SearchDirection.up: nextPixel = GoUp(nextPixel); | |
lastSelectedPos = 0; | |
break; | |
case SearchDirection.upRight: nextPixel = GoUpRight(nextPixel); | |
lastSelectedPos = 1; | |
break; | |
case SearchDirection.right: nextPixel = GoRight(nextPixel); | |
lastSelectedPos = 2; | |
break; | |
case SearchDirection.downRight: nextPixel = GoDownRight(nextPixel); | |
lastSelectedPos = 3; | |
break; | |
case SearchDirection.down: nextPixel = GoDown(nextPixel); | |
lastSelectedPos = 4; | |
break; | |
case SearchDirection.downLeft: nextPixel = GoDownLeft(nextPixel); | |
lastSelectedPos = 5; | |
break; | |
case SearchDirection.left: nextPixel = GoLeft(nextPixel); | |
lastSelectedPos = 6; | |
break; | |
case SearchDirection.upLeft: nextPixel = GoUpLeft(nextPixel); | |
lastSelectedPos = 7; | |
break; | |
} | |
SetPixelEdgeColor(nextPixel, Green); | |
} | |
//----------------------------------------------------------------- | |
// Color all the corners | |
//----------------------------------------------------------------- | |
for (int i = 2; i < cornersFound.Count; i++) | |
{ | |
SetStatus("Found corner @: " + cornersFound[i]); | |
ColorSurroundingPixels(cornersFound[i], 20, EdgeColor); | |
} | |
//Output the edge coordinates | |
//Debug.Log(coordList); | |
SetStatus("Completed tracking an object."); | |
AddBlob(); | |
doCornerCheckRoutine = false; //Close the loop | |
//Apply the tracking data to the image for visualization | |
RecolorImage(); | |
step4 = true; //Start filling the polygon | |
yield break; | |
} | |
} | |
} | |
hasStartedStep1 = false; //Allow loop for current procedure | |
} | |
else | |
{ | |
SetStatus("Searching..."); | |
} | |
yield return 0; | |
} | |
yield return 0; | |
} | |
void SearchNextPixel(Vector2 basePixel) | |
{ | |
Vector2 nextPixel = basePixel; | |
SetStatus("Start identifying polygon"); | |
StartCoroutine(IdentifyCorners(nextPixel)); | |
} | |
void ClearMarks() | |
{ | |
mark1 = false; | |
mark2 = false; | |
mark3 = false; | |
mark4 = false; | |
mark5 = false; | |
mark6 = false; | |
mark7 = false; | |
mark8 = false; | |
mark9 = false; | |
upperHalfCount = 0; | |
lowerHalfCount = 0; | |
} | |
void CheckMatchingColor(int curPos, Vector2 baseCoord) | |
{ | |
Color searchColor = buildingColor; | |
matchingColorCount = 0; | |
if (CompareColors(GetPixelColor(GoUp(baseCoord)), searchColor, matchingColorErrorCorrect)) | |
{ | |
hasSetNextDirection = true; | |
curDirection = SearchDirection.up; | |
matchingColorCount++; | |
} | |
if (CompareColors(GetPixelColor(GoUpRight(baseCoord)), searchColor, matchingColorErrorCorrect)) | |
{ | |
hasSetNextDirection = true; | |
curDirection = SearchDirection.upRight; | |
matchingColorCount++; | |
} | |
if (CompareColors(GetPixelColor(GoRight(baseCoord)), searchColor, matchingColorErrorCorrect)) | |
{ | |
hasSetNextDirection = true; | |
curDirection = SearchDirection.right; | |
matchingColorCount++; | |
} | |
if (CompareColors(GetPixelColor(GoDownRight(baseCoord)), searchColor, matchingColorErrorCorrect)) | |
{ | |
hasSetNextDirection = true; | |
curDirection = SearchDirection.downRight; | |
matchingColorCount++; | |
} | |
if (CompareColors(GetPixelColor(GoDown(baseCoord)), searchColor, matchingColorErrorCorrect)) | |
{ | |
hasSetNextDirection = true; | |
curDirection = SearchDirection.down; | |
matchingColorCount++; | |
} | |
if (CompareColors(GetPixelColor(GoDownLeft(baseCoord)), searchColor, matchingColorErrorCorrect)) | |
{ | |
hasSetNextDirection = true; | |
curDirection = SearchDirection.downLeft; | |
matchingColorCount++; | |
} | |
if (CompareColors(GetPixelColor(GoLeft(baseCoord)), searchColor, matchingColorErrorCorrect)) | |
{ | |
hasSetNextDirection = true; | |
curDirection = SearchDirection.left; | |
matchingColorCount++; | |
} | |
if (CompareColors(GetPixelColor(GoUpLeft(baseCoord)), searchColor, matchingColorErrorCorrect)) | |
{ | |
hasSetNextDirection = true; | |
curDirection = SearchDirection.upLeft; | |
matchingColorCount++; | |
} | |
// return null; | |
} | |
int GetEdgeBasedOnStart(Vector2 baseCoord, Color32 baseColor, int lastPos) | |
{ | |
int identicalPixelCount = 0; | |
ClearMarks(); | |
bool hasSetDirection = false; | |
int totalDirection = 8; | |
int incrementer = 0; | |
for (int i = lastPos; incrementer < totalDirection; i++) | |
{ | |
incrementer++; | |
if (i > totalDirection) | |
i = 0; | |
switch (i) | |
{ | |
case 0: | |
if (baseCoord.y < videoFootage.texture.height) | |
{ | |
if (CompareColors(GetPixelColor(GoUp(baseCoord)), baseColor, 2)) | |
{ | |
identicalPixelCount++; | |
if (!hasSetDirection) | |
{ | |
hasSetDirection = true; | |
curDirection = SearchDirection.up; | |
} | |
upperHalfCount++; | |
mark2 = true; | |
} | |
} | |
else | |
{ | |
//"We are on the top edge"); | |
// hasSetDirection = true; | |
// curDirection = SearchDirection.right; | |
totalErrors++; | |
//identicalPixelCount = 9; | |
} | |
break; | |
case 1: | |
if (baseCoord.y < videoFootage.texture.height && baseCoord.x < videoFootage.texture.width) | |
{ | |
if (CompareColors(GetPixelColor(GoUpRight(baseCoord)), baseColor, 2)) | |
{ | |
identicalPixelCount++; | |
if (!hasSetDirection) | |
{ | |
hasSetDirection = true; | |
curDirection = SearchDirection.upRight; | |
} | |
upperHalfCount++; | |
mark3 = true; | |
} | |
} | |
else | |
{ | |
//("We are on the top edge or top right corner"); | |
// hasSetDirection = true; | |
// curDirection = SearchDirection.down; | |
totalErrors++; | |
// identicalPixelCount = 9; | |
} | |
break; | |
case 2: | |
if (baseCoord.x < videoFootage.texture.width) | |
{ | |
if (CompareColors(GetPixelColor(GoRight(baseCoord)), baseColor, 2)) | |
{ | |
identicalPixelCount++; | |
if (!hasSetDirection) | |
{ | |
hasSetDirection = true; | |
curDirection = SearchDirection.right; | |
} | |
upperHalfCount++; | |
mark4 = true; | |
} | |
} | |
else | |
{ | |
totalErrors++; | |
} | |
break; | |
case 3: | |
if (baseCoord.x < videoFootage.texture.width && baseCoord.y > 0) | |
{ | |
if (CompareColors(GetPixelColor(GoDownRight(baseCoord)), baseColor, 2)) | |
{ | |
identicalPixelCount++; | |
if (!hasSetDirection) | |
{ | |
hasSetDirection = true; | |
curDirection = SearchDirection.downRight; | |
} | |
upperHalfCount++; | |
mark5 = true; | |
} | |
} | |
else | |
{ | |
totalErrors++; | |
} | |
break; | |
case 4: | |
if (baseCoord.y > 0) | |
{ | |
if (CompareColors(GetPixelColor(GoDown(baseCoord)), baseColor, 2)) | |
{ | |
identicalPixelCount++; | |
if (!hasSetDirection) | |
{ | |
hasSetDirection = true; | |
curDirection = SearchDirection.down; | |
} | |
upperHalfCount++; | |
mark6 = true; | |
} | |
} | |
else | |
{ | |
totalErrors++; | |
} | |
break; | |
case 5: | |
if (baseCoord.y > 0 && baseCoord.x > 0) | |
{ | |
if (CompareColors(GetPixelColor(GoDownLeft(baseCoord)), baseColor, 2)) | |
{ | |
identicalPixelCount++; | |
if (!hasSetDirection) | |
{ | |
hasSetDirection = true; | |
curDirection = SearchDirection.downLeft; | |
} | |
lowerHalfCount++; | |
mark7 = true; | |
} | |
} | |
else | |
{ | |
totalErrors++; | |
} | |
break; | |
case 6: | |
if (baseCoord.x > 0) | |
{ | |
if (CompareColors(GetPixelColor(GoLeft(baseCoord)), baseColor, 2)) | |
{ | |
identicalPixelCount++; | |
if (!hasSetDirection) | |
{ | |
hasSetDirection = true; | |
curDirection = SearchDirection.left; | |
} | |
lowerHalfCount++; | |
mark8 = true; | |
} | |
} | |
else | |
{ | |
totalErrors++; | |
} | |
break; | |
case 7: | |
if (baseCoord.x > 0 && baseCoord.y < videoFootage.texture.height) | |
{ | |
if (CompareColors(GetPixelColor(GoUpLeft(baseCoord)), baseColor, 2)) | |
{ | |
identicalPixelCount++; | |
if (!hasSetDirection) | |
{ | |
hasSetDirection = true; | |
curDirection = SearchDirection.upLeft; | |
} | |
lowerHalfCount++; | |
mark1 = true; | |
} | |
} | |
else | |
{ | |
totalErrors++; | |
} | |
break; | |
} | |
} | |
hasSetDirection = false; | |
return identicalPixelCount; | |
} | |
void FinishedColoringEdges() | |
{ | |
Debug.Log("Finished Coloring"); | |
} | |
Vector2 GoRight(Vector2 basePixel) | |
{ | |
return new Vector2(basePixel.x + 1, basePixel.y); | |
} | |
Vector2 GoLeft(Vector2 basePixel) | |
{ | |
return new Vector2(basePixel.x - 1, basePixel.y); | |
} | |
Vector2 GoUp(Vector2 basePixel) | |
{ | |
return new Vector2(basePixel.x, basePixel.y + 1); | |
} | |
Vector2 GoDown(Vector2 basePixel) | |
{ | |
return new Vector2(basePixel.x, basePixel.y - 1); | |
} | |
//Corner pixels | |
Vector2 GoUpRight(Vector2 basePixel) | |
{ | |
return new Vector2(basePixel.x + 1, basePixel.y + 1); | |
} | |
Vector2 GoDownRight(Vector2 basePixel) | |
{ | |
return new Vector2(basePixel.x + 1, basePixel.y - 1); | |
} | |
Vector2 GoUpLeft(Vector2 basePixel) | |
{ | |
return new Vector2(basePixel.x - 1, basePixel.y + 1); | |
} | |
Vector2 GoDownLeft(Vector2 basePixel) | |
{ | |
return new Vector2(basePixel.x - 1, basePixel.y - 1); | |
} | |
void RecolorImage() | |
{ | |
//SetStatus("ColoringPixels"); | |
Texture2D cloned = new Texture2D(videoFootage.texture.width, videoFootage.texture.height); | |
cloned.SetPixels32(imageColors); | |
cloned.Apply(false); | |
videoFootage.texture = cloned; | |
// SetStatus("FinishedColoringPixels"); | |
} | |
//Checks whether 2 colors are similar, within a specified range | |
bool CompareColors(Color32 pixelColor, Color32 checkColor, int tempEfficiency) | |
{ | |
float efficiency = tempEfficiency;// / 255; | |
if ((checkColor.r + efficiency) >= pixelColor.r && (checkColor.r - efficiency) <= pixelColor.r) | |
{ | |
if ((checkColor.g + efficiency) >= pixelColor.g && (checkColor.g - efficiency) <= pixelColor.g) | |
{ | |
if ((checkColor.b + efficiency) >= pixelColor.b && (checkColor.b - efficiency) <= pixelColor.b) | |
{ | |
return true; | |
} | |
else | |
return false; | |
} | |
else | |
return false; | |
} | |
return false; | |
} | |
Mesh CreateMesh(int num) | |
{ | |
int x = 0; //Counter | |
//Create a new mesh | |
Mesh mesh = new Mesh(); | |
List<Vector3> vertices = new List<Vector3>(); | |
for (int i = 0; i < cornersFound.Count; i++) | |
{ | |
vertices.Add(Camera.main.ScreenToWorldPoint(new Vector3(cornersFound[i].x, cornersFound[i].y, Camera.main.nearClipPlane))); //new Vector3(coordsFound[i].x, coordsFound[i].y, 1)); | |
} | |
//UVs | |
var uvs = new Vector2[cornersFound.Count]; | |
for (x = 0; x < cornersFound.Count; x++) | |
{ | |
if ((x % 2) == 0) | |
{ | |
uvs[x] = new Vector2(0, 0); | |
} | |
else | |
{ | |
uvs[x] = new Vector2(1, 1); | |
} | |
} | |
//Triangles | |
var tris = new int[3 * (vertices.Count - 2)]; //3 verts per triangle * num triangles | |
int C1; | |
int C2; | |
int C3; | |
num = 0; | |
if (num == 0) | |
{ | |
C1 = 0; | |
C2 = 1; | |
C3 = 2; | |
for (x = 0; x < tris.Length; x += 3) | |
{ | |
tris[x] = C1; | |
tris[x + 1] = C2; | |
tris[x + 2] = C3; | |
C2++; | |
C3++; | |
} | |
} | |
else | |
{ | |
C1 = 0; | |
C2 = vertices.Count - 1; | |
C3 = vertices.Count - 2; | |
for (x = 0; x < tris.Length; x += 3) | |
{ | |
tris[x] = C1; | |
tris[x + 1] = C2; | |
tris[x + 2] = C3; | |
C2--; | |
C3--; | |
} | |
} | |
//Assign data to mesh | |
mesh.vertices = vertices.ToArray(); | |
mesh.uv = uvs; | |
mesh.triangles = tris; | |
//Recalculations | |
mesh.RecalculateNormals(); | |
mesh.RecalculateBounds(); | |
mesh.Optimize(); | |
//Name the mesh | |
mesh.name = "MyMesh"; | |
//Return the mesh | |
return mesh; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment