{"id":21436928,"url":"https://github.com/willigarneau/object-detection-cuda","last_synced_at":"2025-03-16T23:24:26.531Z","repository":{"id":99252248,"uuid":"148368243","full_name":"willigarneau/object-detection-cuda","owner":"willigarneau","description":"🕺 Put my knowledge of OpenCV and Cuda into practice to create an object detection system. 💻","archived":false,"fork":false,"pushed_at":"2018-09-28T16:03:26.000Z","size":101477,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-23T09:34:33.694Z","etag":null,"topics":["camera","cplusplus","cuda","detector","filter","opencv"],"latest_commit_sha":null,"homepage":"","language":"C++","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/willigarneau.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}},"created_at":"2018-09-11T19:21:34.000Z","updated_at":"2021-12-22T18:17:44.000Z","dependencies_parsed_at":"2023-04-21T15:17:05.226Z","dependency_job_id":null,"html_url":"https://github.com/willigarneau/object-detection-cuda","commit_stats":null,"previous_names":["willigarneau/object-detection-cuda"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willigarneau%2Fobject-detection-cuda","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willigarneau%2Fobject-detection-cuda/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willigarneau%2Fobject-detection-cuda/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willigarneau%2Fobject-detection-cuda/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/willigarneau","download_url":"https://codeload.github.com/willigarneau/object-detection-cuda/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243945859,"owners_count":20372947,"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":["camera","cplusplus","cuda","detector","filter","opencv"],"created_at":"2024-11-23T00:16:50.024Z","updated_at":"2025-03-16T23:24:26.511Z","avatar_url":"https://github.com/willigarneau.png","language":"C++","readme":"  \u003ch1 align=\"center\"\u003eObject Detection - Cuda\u003c/h1\u003e \n  \u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/License-MIT-blue.svg\"\u003e\n  \u003c/p\u003e\n\n\n\u003e Implementation of a motion detection solution with OpenCV and Cuda, as part of the Intelligent Industrial System Course.\n\n### Part 1: Convert an RGB color image to HSV, without using an external library.\n\n#### Here's the algorithm :\nThe R,G,B values are divided by 255 to change the range from 0..255 to 0..1:\u003cbr\u003e\nR' = R/255\u003cbr\u003e\nG' = G/255\u003cbr\u003e\nB' = B/255\u003cbr\u003e\nCmax = max(R', G', B')\u003cbr\u003e\nCmin = min(R', G', B')\u003cbr\u003e\nΔ = Cmax - Cmin\u003cbr\u003e\nHue calculation:\u003cbr\u003e\nhttp://www.rapidtables.com/convert/color/rgb-to-hsv/hue-calc.gif\u003cbr\u003e\nSaturation calculation:\u003cbr\u003e\nhttp://www.rapidtables.com/convert/color/rgb-to-hsv/sat-calc.gif\u003cbr\u003e\nValue calculation: V = Cmax\u003cbr\u003e\n\nThe algorithm running on both the CPU and the GPU has been implemented in both cases, to facilitate understanding of the steps to follow when paralleling in Cuda.\n\n```c++\nvoid rgbToHSV(Mat frame) {\n\tVec3b hsv;\n\tfor (int rows = 0; rows \u003c frame.rows; rows++) {\n\t\tfor (int cols = 0; cols \u003c frame.cols; cols++) {\n\t\t\tfloat blue =  frame.at\u003cVec3b\u003e(rows, cols)[0] / 255.0; // blue\n\t\t\tfloat green = frame.at\u003cVec3b\u003e(rows, cols)[1] / 255.0; // green\n\t\t\tfloat red = frame.at\u003cVec3b\u003e(rows, cols)[2] / 255.0; // red\n\n\t\t\tfloat maximum = MAX3(red, green, blue);\n\t\t\tfloat minimum = MIN3(red, green, blue);\n\n\t\t\tfloat delta = maximum - minimum;\n\t\t\tuchar h = HueConversion(blue, green, red, delta, maximum);\n\t\t\thsv[0] = h / 2;\n\t\t\tuchar s = (delta / maximum) * 255;\n\t\t\thsv[1] = s;\n\t\t\tfloat v = (maximum) * 255;\n\t\t\thsv[2] = v;\n\n\t\t\tframe.at\u003cVec3b\u003e(rows, cols) = hsv;\n\t\t}\n\t}\n}\n```\n\nAlso, here is an example of a way to calculate the hue value in c++ :\n\n```c++\n\nuchar HueConversion(float blue, float green, float red, float delta, float maximum) {\n\tuchar h;\n\tif (red == maximum) { h = 60* (green - blue) / delta; }\n\tif (green == maximum) { h = 60 * (blue - red) / delta + 120; }\n\tif (blue == maximum) { h = 60 * (red - green) / delta + 240; }\n\tif (h \u003c 0) { h += 360; }\n\treturn h;\n}\n```\n\u003e The Hue value you get needs to be multiplied by 60 to convert it to degrees on the color circle. If Hue becomes negative you need to add 360 to, because a circle has 360 degrees.\n\n###### `MAX`/`MIN` and `MAX3`/`MIN` functions were made to calculate highest and lowest value between 2 or 3 parameters like this :\n```c++\n#define MIN(a,b)      ((a) \u003c (b) ? (a) : (b))\n#define MAX(a,b) ((a) \u003e (b) ? (a) : (b))\n#define MIN3(a,b,c)   MIN((a), MIN((b), (c)))\n#define MAX3(a,b,c) MAX((a), MAX((b), (c)))\n```\n\n### Part 2: Convert an HSV frame to Sobel Filter, without using an external library.\n\nThis part has been made in Cuda, to get a quicker result.\n\n```c++\n__global__ void parallelSobelFilter_kernel(uchar* inputImage, uchar* outputImage, int width, int height)\n{\n\tint gIndex = blockIdx.x  * blockDim.x + threadIdx.x; // global index\n\tint tIndex = gIndex - width; // top index\n\tint bIndex = gIndex + width; // bottom index\n\n\tif (tIndex \u003c 0 || bIndex\u003e(width*height)) {\n\t\treturn;\n\t}\n\n\tint gradientX =\n\t\tinputImage[tIndex - 1] * Gx[0][0] + // left top\n\t\tinputImage[gIndex - 1] * Gx[1][0] + // left middle\n\t\tinputImage[bIndex - 1] * Gx[2][0] + // left bottom\n\t\tinputImage[tIndex + 1] * Gx[0][2] + // right top\n\t\tinputImage[gIndex + 1] * Gx[1][2] + // right middle\n\t\tinputImage[bIndex + 1] * Gx[2][2];  // right bottom\n\n\tint gradientY = \n\t\tinputImage[tIndex - 1] * Gy[0][0] + // left top\n\t\tinputImage[tIndex] * Gy[0][1] + // middle top\n\t\tinputImage[bIndex - 1] * Gy[2][0] + // left bottom\n\t\tinputImage[tIndex + 1] * Gy[0][2] + // right top\n\t\tinputImage[bIndex] * Gy[2][1] + // middle bottom\n\t\tinputImage[bIndex + 1] * Gy[2][2];  // right bottom\n\n\tgradientX = gradientX * gradientX;\n\tgradientY = gradientY * gradientY;\n\tfloat approxGradient = sqrtf(gradientX + gradientY + 0.0);\n\tif (approxGradient \u003e 255) { \n\t\tapproxGradient = 255;\n\t}\n\toutputImage[gIndex] = (uchar)approxGradient;\n}\n```\n\nThis part of code has been generated by a kernel, which was called in our main CPU file :\n\n```c++\nextern \"C\" cudaError_t parallelSobelFilter(Mat *inputImage, Mat *outputImage) {\n\tcudaError_t status;\n\tuchar *inputSobel, *outputSobel;\n\t// 0. Define block dimension\n\tint BLOCK_COUNT = iDivUp((inputImage-\u003ecols * inputImage-\u003erows), BLOCK_SIZE);\n\t// 1. Define input/output image sizes\n\tuint imgSize = inputImage-\u003erows * inputImage-\u003estep1();\n\tuint gradientSize = inputImage-\u003erows * inputImage-\u003ecols * sizeof(uchar);\n\t// 2. Allocate memory space for both matrix on gpu\n\tstatus = cudaMalloc(\u0026inputSobel, imgSize);\n\tif (status != cudaSuccess)\n\t{\n\t\tfprintf(stderr, \"cudaMalloc failed\");\n\t\tgoto Error;\n\t}\n\tcudaMalloc(\u0026outputSobel, gradientSize);\n\tif (status != cudaSuccess)\n\t{\n\t\tfprintf(stderr, \"cudaMalloc failed\");\n\t\tgoto Error;\n\t}\n\t// 3. Send matrix(A) to gpu\n\tstatus = cudaMemcpy(inputSobel, inputImage-\u003edata, imgSize, cudaMemcpyHostToDevice);\n\tif (status != cudaSuccess)\n\t{\n\t\tfprintf(stderr, \"cudaMemcpy failed\");\n\t\tgoto Error;\n\t}\n\t// 4. Treat matrix in sobel filter kernel\n\tparallelSobelFilter_kernel\u003c\u003c\u003cBLOCK_COUNT, BLOCK_SIZE\u003e\u003e\u003e(inputSobel, outputSobel, inputImage-\u003ecols, inputImage-\u003erows);\n\t// 5. Wait for the kernel to end\n\tstatus = cudaDeviceSynchronize();\n\tif (status != cudaSuccess)\n\t{\n\t\tfprintf(stderr, \"cudaDeviceSynchronize failed\");\n\t\tgoto Error;\n\t}\n\t// 6. Transfer result matrix to output image\n\tstatus = cudaMemcpy(outputImage-\u003edata, outputSobel, gradientSize, cudaMemcpyDeviceToHost);\n\tif (status != cudaSuccess)\n\t{\n\t\tfprintf(stderr, \"cudaMemcpy failed\");\n\t\tgoto Error;\n\t}\n\t// 7. Free matrix in memory\n\tcudaFree(inputSobel);\n\tcudaFree(outputSobel);\nError:\n\tcudaFree(inputSobel);\n\tcudaFree(outputSobel);\n\n\treturn status;\n}\n\n```\n\n\n#### This project is currently under development. If you have good ideas, do not hesitate to contribute! 😊\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilligarneau%2Fobject-detection-cuda","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwilligarneau%2Fobject-detection-cuda","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilligarneau%2Fobject-detection-cuda/lists"}