{"id":21373729,"url":"https://github.com/ashislaha/Shape-Detection-in-AR","last_synced_at":"2025-07-13T08:30:38.597Z","repository":{"id":67308654,"uuid":"105252183","full_name":"ashislaha/Shape-Detection-in-AR","owner":"ashislaha","description":"Detect the shape of drawing objects (classes - line, triangle, rectangle, pentagon, Hexagon, circle) and draw in Augmented Reality.","archived":false,"fork":false,"pushed_at":"2018-02-07T02:14:43.000Z","size":6794,"stargazers_count":59,"open_issues_count":0,"forks_count":22,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-22T08:37:42.199Z","etag":null,"topics":["coreml-ar","edge-detection-coreml-model","opencv","shape-detection","shape-detection-ar","shape-detection-coreml","shape-detection-in-ar","shape-detection-ml","shape-detection-opencv"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ashislaha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-09-29T09:01:32.000Z","updated_at":"2024-10-27T16:27:41.000Z","dependencies_parsed_at":"2023-03-17T14:45:49.280Z","dependency_job_id":null,"html_url":"https://github.com/ashislaha/Shape-Detection-in-AR","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ashislaha/Shape-Detection-in-AR","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashislaha%2FShape-Detection-in-AR","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashislaha%2FShape-Detection-in-AR/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashislaha%2FShape-Detection-in-AR/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashislaha%2FShape-Detection-in-AR/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ashislaha","download_url":"https://codeload.github.com/ashislaha/Shape-Detection-in-AR/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashislaha%2FShape-Detection-in-AR/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265109673,"owners_count":23712730,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["coreml-ar","edge-detection-coreml-model","opencv","shape-detection","shape-detection-ar","shape-detection-coreml","shape-detection-in-ar","shape-detection-ml","shape-detection-opencv"],"created_at":"2024-11-22T08:29:29.748Z","updated_at":"2025-07-13T08:30:38.587Z","avatar_url":"https://github.com/ashislaha.png","language":"Swift","funding_links":[],"categories":["OOM-Leaks-Crash"],"sub_categories":["AR-VR"],"readme":"# Shape Detection and Drawing in Augmented Reality(AR)\n\nDetect the shape of drawing objects (classes - line, triangle, rectangle, pentagon, Hexagon, circle) and draw in Augmented Reality. \nAlso mention the shape type along with drawing. \n## Like : \"L\" - Line, \"T\" - Triangle , \"R\" - Rectangle, \"C\" - Circle, \"P\" - Pentagon, \"H\" - Hexagon\n\n## iTunes Link : https://itunes.apple.com/us/app/shape-detection-ar/id1292244288?ls=1\u0026mt=8\n\n# Input Image : \n![img_2662](https://user-images.githubusercontent.com/10649284/31043178-4a9bf024-a5d5-11e7-849d-e88e00c9f7a5.JPG)\n\n# Edge Detected Image : \n![img_0225](https://user-images.githubusercontent.com/10649284/31043244-59f29ebe-a5d6-11e7-87a6-6ca9209d961f.JPG)\n\n# Find Contour \u0026 Fill it for visualization :\n![img_0226](https://user-images.githubusercontent.com/10649284/31043257-8855e70c-a5d6-11e7-8a16-c2a4afee93a9.JPG)\n\n# Create Scene graph :\n![img_4427d21af932-1](https://user-images.githubusercontent.com/10649284/31043182-53017ad6-a5d5-11e7-9067-3100c58808c2.jpeg)\n\n# Example : \n![2](https://user-images.githubusercontent.com/10649284/31055884-634d54ce-a6e7-11e7-91b8-59fd820321f3.JPG)\n![4](https://user-images.githubusercontent.com/10649284/31056054-7c2a42c0-a6e9-11e7-8672-ea91ea8a5b2f.jpeg)\n \n## Basic Steps : \n\n## step 1 : Create a mlmodel for Edge Detection (Generic type) \n\n## step 2 : Take the image from ARFrame \u0026 idenfify edges using edge_detection.mlmodel\n\n## step 3 : Find out the Contours in the edge_detected image \u0026 calculate the approximation points using openCV.\n\n## step 4 : Figure the Shapes with it's image co-ordinates from Approximation points \n\n## step 5 : Map the image co-ordinates of shapes into AR-world co-ordinates\n\n## step 6 : Render Scene Graph \n\n# In the project, pod is not installed, So please do, \"$pod install\" before running the project.\n\n-----------------------------------------------------------\n# Create an Edge Detection CoreML model \n-----------------------------------------------------------\n\nOriginal Caffe Model : http://vcl.ucsd.edu/hed/hed_pretrained_bsds.caffemodel\n\nThe Github project is : https://github.com/s9xie/hed\n\nDownload Edge_detection CoreML model(58MB) from : https://drive.google.com/drive/folders/0B0QC-w3ZqaT1ZEtpSG5HOE5VWEk  which contains 6 different type of Outputs. \n\nI am using the Side-out of original model (dsn3 output) only to reduce the space complexity. \n\n(virtualenv2.7) C02QP68UG8WP:CoreML creation ashis.laha$ python mlmodel_converter.py \n\n================= Starting Conversion from Caffe to CoreML ======================\n\nLayer 0: Type: 'Input', Name: 'input'. Output(s): 'data'.\n\nIgnoring batch size and retaining only the trailing 3 dimensions for conversion. \n\nLayer 1: Type: 'Convolution', Name: 'conv1_1'. Input(s): 'data'. Output(s): 'conv1_1'.\n\nLayer 2: Type: 'ReLU', Name: 'relu1_1'. Input(s): 'conv1_1'. Output(s): 'conv1_1'.\n\nLayer 3: Type: 'Convolution', Name: 'conv1_2'. Input(s): 'conv1_1'. Output(s): 'conv1_2'.\n\nLayer 4: Type: 'ReLU', Name: 'relu1_2'. Input(s): 'conv1_2'. Output(s): 'conv1_2'.\n\nLayer 5: Type: 'Pooling', Name: 'pool1'. Input(s): 'conv1_2'. Output(s): 'pool1'.\n\nLayer 6: Type: 'Convolution', Name: 'conv2_1'. Input(s): 'pool1'. Output(s): 'conv2_1'.\n\nLayer 7: Type: 'ReLU', Name: 'relu2_1'. Input(s): 'conv2_1'. Output(s): 'conv2_1'.\n\nLayer 8: Type: 'Convolution', Name: 'conv2_2'. Input(s): 'conv2_1'. Output(s): 'conv2_2'.\n\nLayer 9: Type: 'ReLU', Name: 'relu2_2'. Input(s): 'conv2_2'. Output(s): 'conv2_2'.\n\nLayer 10: Type: 'Pooling', Name: 'pool2'. Input(s): 'conv2_2'. Output(s): 'pool2'.\n\nLayer 11: Type: 'Convolution', Name: 'conv3_1'. Input(s): 'pool2'. Output(s): 'conv3_1'.\n\nLayer 12: Type: 'ReLU', Name: 'relu3_1'. Input(s): 'conv3_1'. Output(s): 'conv3_1'.\n\nLayer 13: Type: 'Convolution', Name: 'conv3_2'. Input(s): 'conv3_1'. Output(s): 'conv3_2'.\n\nLayer 14: Type: 'ReLU', Name: 'relu3_2'. Input(s): 'conv3_2'. Output(s): 'conv3_2'.\n\nLayer 15: Type: 'Convolution', Name: 'conv3_3'. Input(s): 'conv3_2'. Output(s): 'conv3_3'.\n\nLayer 16: Type: 'ReLU', Name: 'relu3_3'. Input(s): 'conv3_3'. Output(s): 'conv3_3'.\n\nLayer 17: Type: 'Convolution', Name: 'score-dsn3'. Input(s): 'conv3_3'. Output(s): 'score-dsn3'.\n\nLayer 18: Type: 'Deconvolution', Name: 'upsample_4'. Input(s): 'score-dsn3'. Output(s): 'score-dsn3-up'.\n\nLayer 19: Type: 'Crop', Name: 'crop'. Input(s): 'score-dsn3-up', 'data'. Output(s): 'upscore-dsn3'.\n\n\n================= Summary of the conversion: ===================================\nDetected input(s) and shape(s) (ignoring batch size):\n\n'data' : 3, 500, 500\n\nNetwork Input name(s): 'data'.\n\nNetwork Output name(s): 'upscore-dsn3'.\n\ninput {\n  name: \"data\"\n  shortDescription: \"Input image to be edge-detected. Must be exactly 500x500 pixels.\"\n  type {\n    imageType {\n      width: 500\n      height: 500\n      colorSpace: BGR\n    }\n  }\n}\n\noutput {\n  name: \"upscore-dsn3\"\n  type {\n    multiArrayType {\n      dataType: DOUBLE\n    }\n  }\n}\n\nmetadata {\n  shortDescription: \"Holistically-Nested Edge Detection. https://github.com/s9xie/hed \"\n  author: \"Original paper: Xie, Saining and Tu, Zhuowen. Caffe implementation: Yangqing Jia. CoreML port: Ashis Laha\"\n  license: \"Unknown\"\n}\n\n--------------------------------------------------------------------------\n### Use the CoreML Model for detecting Edge of ARFrame captured Camera Image\n\n![img_2662](https://user-images.githubusercontent.com/10649284/31043178-4a9bf024-a5d5-11e7-849d-e88e00c9f7a5.JPG)\n\n![img_0225](https://user-images.githubusercontent.com/10649284/31043244-59f29ebe-a5d6-11e7-87a6-6ca9209d961f.JPG)\n\n# Open CV framework added : \n\n## step 1 : create pod file with : pod 'OpenCV'\n\n## step 2 : Create a bridging header \n\tCreate an objective-c file from “Cocoa-touch class”\n\tname it - OpenCVWrapper \n\tXcode is smart and proposes to create a bridging header. Click on Create Bridging Header.\n\n## step 3 : Configure the bridging header ($project_name-Bridging-Header.h)\n\t#import \"OpenCVWrapper.h\" in the bridging header \n\n## step 4 : Change to Objective-c++ \n\tchange from OpenCVWrapper.m to OpenCVWrapper.mm\n\n## step 5 : Importing opencv\n\t#import \u003copencv2/opencv.hpp\u003e\n\t#import \"OpenCVWrapper.h\"\n\tinto OpenCVWrapper.mm file. \n\t\n### NOTED : You will get ERROR : enum { NO, FEATHER, MULTI_BAND }; because of “NO” enum name. #import \u003copencv2/opencv.hpp\u003e above all other imports will resolve the issue.\n\n## Step 6 : Write a test code \n\n\tIn OpenCVWrapper.h —\u003e -(void) isOpenCVWorking;\n\tIn OpenCVWrapper.mm —\u003e  @Implementation \n\n\t-(void) isOpenCVWorking {\n    \t     NSLog(@\"It's working\");\n\t }\n\n\t@end\n\nAND call this from Swift class like :\n\t\n\tlet openCVWrapper = OpenCVWrapper()\n    \topenCVWrapper.isOpenCVWorking()\n\nIt will generate Output :  \"It's working”\n\n\n# Finding Shape :\n\n## step1 : convert image into CV Matrix \n\n    +(cv::Mat)CVMatFromImage:(UIImage *)image {\n    \n    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);\n    size_t numberOfComponents = CGColorSpaceGetNumberOfComponents(colorSpace);\n    CGFloat cols = image.size.width;\n    CGFloat rows = image.size.height;\n    \n    cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels\n    CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipLast | kCGBitmapByteOrderDefault;\n    \n    // check whether the UIImage is greyscale already\n    if (numberOfComponents == 1){\n        cvMat = cv::Mat(rows, cols, CV_8UC1); // 8 bits per component, 1 channels\n        bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault;\n    }\n    \n    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to backing data\n                                                    cols,                       // Width of bitmap\n                                                    rows,                       // Height of bitmap\n                                                    8,                          // Bits per component\n                                                    cvMat.step[0],              // Bytes per row\n                                                    colorSpace,                 // Colorspace\n                                                    bitmapInfo);                // Bitmap info flags\n    \n    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);\n    CGContextRelease(contextRef);\n    \n    return cvMat;\n}\n\n\n## step 2 : apply Morphology Transformations\n\n## step 3 : Find Contour from Image \n\n## step 4 : Calculate Approximate points from Contour \n\n## step 5 : Based on Approximation size, define the shape \n\n## step 6 : Retrieve the Positions (co-ordinates), Radius, Center of Circle \u0026 other shapes \n\n    -(cv::Mat) shapeDetection :(UIImage *)image { // image is the result of Edge detection, it's in gray scale.\n    \n    /*\n     // Convert to grayscale\n     cv::Mat gray;\n     cv::cvtColor(src, gray, CV_BGR2GRAY);\n     // Convert to binary image using Canny\n     cv::Mat bw;\n     cv::Canny(gray, bw, 0, 50, 5);\n     imageView.image = [UIImage fromCVMat:gray];\n     */\n    \n    cv::Mat cameraFeed =  [OpenCVWrapper CVMatFromImage:image];\n    std::vector\u003c std::vector\u003ccv::Point\u003e \u003e contours;\n    std::vector\u003ccv::Vec4i\u003e hierarchy;\n    \n    // before applying contour finding, apply Morphology Transformations\n    \n    // Closing the image (Method-1)\n    cv:: Mat bw2;\n    cv:: Mat erodedBW2;\n    cv:: Mat se = getStructuringElement(0, cv::Size(5,5));\n    cv::dilate(cameraFeed, bw2, se);\n    cv::erode(bw2, erodedBW2, se);\n    \n    // Closing the image (Method-2)\n    cv::morphologyEx(cameraFeed, erodedBW2, cv::MORPH_CLOSE, se);\n    \n    // Find contour\n    findContours( cameraFeed, contours, hierarchy, CV_RETR_EXTERNAL,  CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));\n    \n    bool objectFound = false;\n    if (hierarchy.size() \u003e 0) {\n        \n        for (int index = 0; index \u003e= 0; index = hierarchy[index][0]) {\n            cv::Moments moment = moments((cv::Mat)contours[index]);\n            double area = moment.m00;\n            objectFound = (area \u003e 100)? true : false;\n        }\n        //let user know you found an object\n        if(objectFound ==true){\n            for(int i=0; i \u003c contours.size() ; i++) {\n                cv::drawContours(cameraFeed,contours,i,cvScalar(80,255,255),CV_FILLED);\n            }\n        }\n        \n        // let's infer the shape from contours , calculate approx length of contours\n        std::vector\u003ccv::Point\u003e approx;\n        for(int i = 0; i \u003c contours.size(); i++) {\n            cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true)*0.02, true);\n            \n            // Skip small\n            if (!(std::fabs(cv::contourArea(contours[i])) \u003c 100)) { // \u0026\u0026 cv::isContourConvex(approx)\n                \n                printf(\"\\n\\n\\n .......Area : %.0f\\t\", std::fabs(cv::contourArea(contours[i])));\n                \n                cv::Point2f center;\n                float radius = 0.0;\n                NSString * shape = @\"\";\n                \n                switch (approx.size()) {\n                    case 2: // line\n                        printf(\"Line\");\n                        shape = @\"line\";\n                    case 3: // Triangle\n                        printf(\"Triangle\");\n                        shape = @\"triangle\";\n                        break;\n                    case 4: // Rectangle\n                        printf(\"Rectangle\");\n                        shape = @\"rectangle\";\n                        break;\n                    case 5: // Pentagon\n                        printf(\"Pentagon\");\n                        shape = @\"pentagon\";\n                        break;\n                    case 6: //Hexagon\n                        printf(\"Hexagon\");\n                        shape = @\"hexagon\";\n                        break;\n                    default: // circle\n                        printf(\"circle \\t\");\n                        shape = @\"circle\";\n                        cv::minEnclosingCircle(cv::Mat(contours[i]), center, radius);\n                        printf(\"Approx size : %ld , radius = %.1f\",approx.size(),radius);\n                }\n                \n                NSMutableArray * positions = [[NSMutableArray alloc] init];\n                \n                if ([shape isEqual:@\"circle\"]) {\n                    NSDictionary * dict = @{    @\"radius\":  [NSNumber numberWithFloat:radius],\n                                                @\"center.x\":[NSNumber numberWithFloat:center.x],\n                                                @\"center.y\":[NSNumber numberWithFloat:center.y]\n                                                };\n                    [positions addObject:dict];\n                }\n                for (int j = 0; j \u003c approx.size(); j++) {\n                    NSDictionary * dict = @{ @\"x\":[NSNumber numberWithInt:approx[j].x], @\"y\":[NSNumber numberWithInt:approx[j].y]};\n                    [positions addObject:dict];\n                }\n                [self.shapesResults addObject:@{shape:positions}]; // update the dictionary\n            }\n        }\n    }\n    return cameraFeed;\n}\n\n\n## step 7 : Convert cv::Matrix into UIImage again \n\n    +(UIImage *)ImageFromCVMat:(cv::Mat)cvMat {\n    NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()];\n    \n    CGColorSpaceRef colorSpace;\n    CGBitmapInfo bitmapInfo;\n    \n    if (cvMat.elemSize() == 1) {\n        colorSpace = CGColorSpaceCreateDeviceGray();\n        bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault;\n    } else {\n        colorSpace = CGColorSpaceCreateDeviceRGB();\n        bitmapInfo = kCGBitmapByteOrder32Little | (cvMat.elemSize() == 3? kCGImageAlphaNone : kCGImageAlphaNoneSkipFirst);\n    }\n    \n    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);\n    \n    // Creating CGImage from cv::Mat\n    CGImageRef imageRef = CGImageCreate(\n                                        cvMat.cols,                 //width\n                                        cvMat.rows,                 //height\n                                        8,                          //bits per component\n                                        8 * cvMat.elemSize(),       //bits per pixel\n                                        cvMat.step[0],              //bytesPerRow\n                                        colorSpace,                 //colorspace\n                                        bitmapInfo,                 //bitmap info\n                                        provider,                   //CGDataProviderRef\n                                        NULL,                       //decode\n                                        false,                      //should interpolate\n                                        kCGRenderingIntentDefault   //intent\n                                        );\n    \n    // Getting UIImage from CGImage\n    UIImage *finalImage = [UIImage imageWithCGImage:imageRef];\n    CGImageRelease(imageRef);\n    CGDataProviderRelease(provider);\n    CGColorSpaceRelease(colorSpace);\n    \n    return finalImage;\n}\n\n\n## Step 8 : Save the result for Visualization \n\n![img_0197](https://user-images.githubusercontent.com/10649284/31007763-3fcaea76-a51f-11e7-9c5f-a1ad51ee0467.JPG)\n![img_0196](https://user-images.githubusercontent.com/10649284/31007762-3faa72aa-a51f-11e7-918e-191d55bde391.JPG)\n\n    cv::Mat cameraFeed = [self shapeDetection:image];\n    UIImage * result = [OpenCVWrapper ImageFromCVMat:cameraFeed];\n    \n    // save it into photo-galary\n    UIImage * rotatedImage = [[UIImage alloc] initWithCGImage:[result CGImage] scale:1.0 orientation:UIImageOrientationRight];\n    UIImageWriteToSavedPhotosAlbum(rotatedImage, self, nil, nil);\n    \n\n# Co-ordinate Mapping \u0026 SCNNode Create :\n\n![img_4427d21af932-1](https://user-images.githubusercontent.com/10649284/31042288-ff9c0d1a-a5c1-11e7-80f5-f7e638ddaafa.jpeg)\n\n## step 1 : Create A Straight Line : \n\n    class func createline(from : SCNVector3 , to : SCNVector3) -\u003e SCNNode { // Z is static\n        // calculate Angle\n        let dx = from.x - to.x\n        let dy = (from.y - to.y)\n        var theta = atan(Double(dy/dx))\n        if theta == .nan {\n            theta = 3.14159265358979 / 2 // 90 Degree\n        }\n        \n        //Create Node\n        let width = CGFloat(sqrt( dx*dx + dy*dy ))\n        let height : CGFloat = 0.01\n        let length : CGFloat = 0.08\n        let chamferRadius : CGFloat = 0.01\n        let route = SCNBox(width: width, height: height, length: length, chamferRadius: chamferRadius)\n        route.firstMaterial?.diffuse.contents = UIColor.getRandomColor()\n        let midPosition = SCNVector3Make((from.x+to.x)/2, (from.y+to.y)/2,0)\n        let node = SCNNode(geometry: route)\n        node.position = midPosition\n        node.rotation = SCNVector4Make(0, 0, 1, Float(theta)) // along Z axis\n        return node\n    }\n\n## step 2 : Create A Circle :\n\n    class func createCircle(center : SCNVector3, radius : CGFloat) -\u003e SCNNode {\n        var geometry : SCNGeometry!\n        geometry = SCNCylinder(radius: radius, height: 0.01)\n        geometry.firstMaterial?.diffuse.contents = UIColor.getRandomColor()\n        geometry.firstMaterial?.specular.contents = UIColor.getRandomColor()\n        let node = SCNNode(geometry: geometry)\n        node.position = center\n        node.rotation = SCNVector4Make(1, 0, 0, Float(Double.pi/2)) // along X axis\n        return node\n    }\n\n## Step 3 : Create a Boundary : \n\n\tclass func boundaryNode() -\u003e SCNNode {\n        \tlet node = SCNNode()\n        \tlet points : [(Float,Float)] = [(0.0,0.0),(0.5,0.0), (0.5,0.5), (0.0,0.5)]\n        \n        \tfor i in 0..\u003c4 {\n          \t  \tlet x1 = points[i].0\n            \t\tlet y1 = points[i].1\n            \t\tlet x2 = points[(i+1)%points.count].0\n            \t\tlet y2 = points[(i+1)%points.count].1\n            \n            \t\tlet from = SCNVector3Make(x1,y1,0)\n            \t\tlet to = SCNVector3Make(x2,y2,0)\n            \t\tnode.addChildNode(SceneNodeCreator.createline(from: from, to: to))\n        \t}\n      \t   return node\n    \t}\n\n## Step 4 : Map from Image Co-ordinates into AR-Cordinates :\n\nThe Image Co-ordinates looks like : \n\n\t.......Area : 9656\tTriangle\n\t .......Area : 17871\tRectangle\n\t .......Area : 9368\tcircle \tApprox size : 8 , radius = 76.6\n \t.......Area : 3100\tRectangle\n\n\tShape : triangle Values : (\n        \t{ x = 198; y = 255; },\n        \t{ x = 119; y = 373; },\n        \t{ x = 208; y = 485; })\n\n\n\tShape : rectangle Values : (\n        { x = 303; y = 128; },\n        { x = 231; y = 162; },\n        { x = 247; y = 367; },\n        { x = 330; y = 349; })\n\n\n\tShape : circle Values : (\n        { \"center.x\" = 151; \"center.y\" = \"106.5523\"; radius = \"76.61115\"; },\n        { x = 148; y = 30; },\n        { x = 115;  y = 77; },\n        { x = 112; y = 118; },\n        { x = 127; y = 169; },\n        { x = 156; y = 183; },\n        { x = 183; y = 152; },\n        { x = 191; y = 95;  },\n        { x = 186; y = 60;  })\n\n\n\tShape : rectangle Values : (\n        { x = 499; y = 0; },\n        { x = 2; y = 0; },\n        { x = 0; y = 499; },\n        { x = 5; y = 8; })\n\nThe convertion function :\n\n\tclass func getSceneNode(shapreResults : [[String : Any]] ) -\u003e SCNScene { // input is array of dictionary\n        let scene = SCNScene()\n        let convertionRatio : Float = 1000.0\n        let imageWidth : Int = 499\n        let xMin = 10\n        let xMax = 490\n        \n        for eachShape in shapreResults {\n            if let dictionary = eachShape.first {\n                \n                let values = dictionary.value as! [[String : Any]]\n                switch dictionary.key {\n                case \"circle\" :\n                    \n                    if let circleParams = values.first as? [String : Float] {\n                        let x = circleParams[\"center.x\"] ?? 0.0\n                        let y = circleParams[\"center.y\"] ?? 0.0\n                        let radius = circleParams[\"radius\"] ?? 0.0\n                        let center = SCNVector3Make(Float(Float(imageWidth)-y)/convertionRatio+SceneNodeCreator.windowRoot.x, Float(Float(imageWidth)-x)/convertionRatio+SceneNodeCreator.windowRoot.y, SceneNodeCreator.z)\n                        scene.rootNode.addChildNode(SceneNodeCreator.createCircle(center: center, radius: CGFloat(radius/convertionRatio)))\n                        \n                        // adding text\n                        var textPosition = center\n                        textPosition.y = textPosition.y + (radius/convertionRatio) + 0.01\n                        scene.rootNode.addChildNode(SceneNodeCreator.create3DText(\"C\", position: textPosition))\n                        \n                    }\n                    \n                case \"line\",\"triangle\", \"rectangle\",\"pentagon\",\"hexagon\":\n                    for i in 0..\u003cvalues.count { // connect all points usning straight lines (basic)\n                        let x1 = values[i][\"x\"] as! Int\n                        let y1 = values[i][\"y\"] as! Int\n                        let x2 = values[(i+1)%values.count][\"x\"] as! Int\n                        let y2 = values[(i+1)%values.count][\"y\"] as! Int\n                        \n                        // skip the boundary Rectangle here\n                        if x1\u003exMin \u0026\u0026 x1\u003cxMax {\n                            let from = SCNVector3Make(Float(imageWidth-y1)/convertionRatio+SceneNodeCreator.windowRoot.x, Float(imageWidth-x1)/convertionRatio+SceneNodeCreator.windowRoot.y, SceneNodeCreator.z)\n                            let to = SCNVector3Make(Float(imageWidth-y2)/convertionRatio+SceneNodeCreator.windowRoot.x, Float(imageWidth-x2)/convertionRatio+SceneNodeCreator.windowRoot.y, SceneNodeCreator.z)\n                            scene.rootNode.addChildNode(SceneNodeCreator.createline(from: from, to: to))\n                        }\n                    }\n                    \n                    // add shape description\n                    switch values.count {\n                    case 2: // line\n                        let x1 = values[0][\"x\"] as! Int\n                        let y1 = values[0][\"y\"] as! Int\n                        let x2 = values[1][\"x\"] as! Int\n                        let y2 = values[1][\"y\"] as! Int\n                        \n                        if x1\u003exMin \u0026\u0026 x1\u003cxMax {\n                            let center = SceneNodeCreator.center(diagonal_p1: (Float(x1),Float(y1)), diagonal_p2: (Float(x2),Float(y2)))\n                            let centerVector = SCNVector3Make((Float(imageWidth)-center.1)/convertionRatio+SceneNodeCreator.windowRoot.x+0.01,\n                                                              (Float(imageWidth)-center.0)/convertionRatio+SceneNodeCreator.windowRoot.y+0.01,\n                                                              SceneNodeCreator.z)\n                            scene.rootNode.addChildNode(SceneNodeCreator.create3DText(\"L\", position: centerVector))\n                        }\n                        \n                    case 3 : // traingle\n                        let x1 = values[0][\"x\"] as! Int\n                        let y1 = values[0][\"y\"] as! Int\n                        let x2 = values[1][\"x\"] as! Int\n                        let y2 = values[1][\"y\"] as! Int\n                        let x3 = values[2][\"x\"] as! Int\n                        let y3 = values[2][\"y\"] as! Int\n                        \n                        if x1\u003exMin \u0026\u0026 x1\u003cxMax {\n                            let centroid = SceneNodeCreator.centroidOfTriangle(point1: (Float(x1),Float(y1)), point2: (Float(x2),Float(y2)), point3: (Float(x3),Float(y3)))\n                            let centerVector = SCNVector3Make((Float(imageWidth)-centroid.1)/convertionRatio+SceneNodeCreator.windowRoot.x,\n                                                              (Float(imageWidth)-centroid.0)/convertionRatio+SceneNodeCreator.windowRoot.y,\n                                                              SceneNodeCreator.z)\n                            scene.rootNode.addChildNode(SceneNodeCreator.create3DText(\"T\", position: centerVector))\n                        }\n                        \n                    case 4: // Rectangle\n                        let x1 = values[0][\"x\"] as! Int\n                        let y1 = values[0][\"y\"] as! Int\n                        let x2 = values[2][\"x\"] as! Int\n                        let y2 = values[2][\"y\"] as! Int\n                        if x1\u003exMin \u0026\u0026 x1\u003cxMax {\n                            let center = SceneNodeCreator.center(diagonal_p1: (Float(x1),Float(y1)), diagonal_p2: (Float(x2),Float(y2)))\n                            let centerVector = SCNVector3Make((Float(imageWidth)-center.1)/convertionRatio+SceneNodeCreator.windowRoot.x,\n                                                              (Float(imageWidth)-center.0)/convertionRatio+SceneNodeCreator.windowRoot.y,\n                                                              SceneNodeCreator.z)\n                            scene.rootNode.addChildNode(SceneNodeCreator.create3DText(\"R\", position: centerVector))\n                        }\n                    case 5,6: // pentagon, Hexagon\n                        let x1 = values[0][\"x\"] as! Int\n                        let y1 = values[0][\"y\"] as! Int\n                        let x2 = values[3][\"x\"] as! Int\n                        let y2 = values[3][\"y\"] as! Int\n                        \n                        if x1\u003exMin \u0026\u0026 x1\u003cxMax {\n                            let center = SceneNodeCreator.center(diagonal_p1: (Float(x1),Float(y1)), diagonal_p2: (Float(x2),Float(y2)))\n                            let centerVector = SCNVector3Make((Float(imageWidth)-center.1)/convertionRatio+SceneNodeCreator.windowRoot.x,\n                                                              (Float(imageWidth)-center.0)/convertionRatio+SceneNodeCreator.windowRoot.y,\n                                                              SceneNodeCreator.z)\n                            let text = (values.count == 5) ? \"P\" : \"H\"\n                            scene.rootNode.addChildNode(SceneNodeCreator.create3DText(text, position: centerVector))\n                        }\n                    default:\n                        print(\"NO Shape\")\n                    }\n                    \n                default :\n                    print(\"This is default for Drawing node \")\n                }\n            }\n        }\n        // add boundary\n        scene.rootNode.addChildNode(SceneNodeCreator.boundaryNode())\n        return scene\n    }\n\n\n![2](https://user-images.githubusercontent.com/10649284/31055884-634d54ce-a6e7-11e7-91b8-59fd820321f3.JPG)\n![4](https://user-images.githubusercontent.com/10649284/31056054-7c2a42c0-a6e9-11e7-8672-ea91ea8a5b2f.jpeg)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fashislaha%2FShape-Detection-in-AR","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fashislaha%2FShape-Detection-in-AR","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fashislaha%2FShape-Detection-in-AR/lists"}