{"id":26905781,"url":"https://github.com/jongomez/numgl","last_synced_at":"2025-04-01T10:58:55.468Z","repository":{"id":25883644,"uuid":"29323994","full_name":"jongomez/numgl","owner":"jongomez","description":"Image processing with WebGL.","archived":false,"fork":false,"pushed_at":"2020-10-16T14:59:36.000Z","size":35,"stargazers_count":87,"open_issues_count":0,"forks_count":5,"subscribers_count":7,"default_branch":"master","last_synced_at":"2023-02-27T18:56:20.583Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jongomez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-15T23:47:48.000Z","updated_at":"2022-10-24T18:38:48.000Z","dependencies_parsed_at":"2022-08-24T01:01:01.368Z","dependency_job_id":null,"html_url":"https://github.com/jongomez/numgl","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jongomez%2Fnumgl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jongomez%2Fnumgl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jongomez%2Fnumgl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jongomez%2Fnumgl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jongomez","download_url":"https://codeload.github.com/jongomez/numgl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246628229,"owners_count":20808106,"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":[],"created_at":"2025-04-01T10:58:54.921Z","updated_at":"2025-04-01T10:58:55.462Z","avatar_url":"https://github.com/jongomez.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NumGL\n\n- Apply image processing algorithms using WebGL.\n- Supports pictures, videos, webcam streams and user defined arrays.\n- Everything is sent to the fragment shader as 2D textures.\n- Let fragment shaders handle the hard work.\n- \u003ca href=\"https://jongomez.github.io/post/numgl/\"\u003eBlog post.\u003c/a\u003e\n\n## Docs\n\n### Store everything as a texture\n\n---\n\n- The store_* functions return a storageId, which is passed into the processing functions.\n\n```javascript\nwindow.onload = function() {\n\t// Send the picture to WebGL as texture. To do this with video/webcam...\n\t// ... change store_picture to store_video / store_webcam.\n\tvar storageId = numgl.store_picture(\"imgElementID\");\n\tnumgl.show_texture(storageId);\n}\n```\n\n---\n\n### Greyscaling images\n\n---\n\n```javascript\nnumgl.grey(storageId);\n```\n\nFiddle with it:\n\n- \u003ca href=\"http://jsfiddle.net/jongomez/q3n5gj7u/\"\u003eImage\u003c/a\u003e\n- \u003ca href=\"http://jsfiddle.net/sputro0e/\"\u003eVideo\u003c/a\u003e\n- \u003ca href=\"https://jsfiddle.net/wm4gr6co/1/\"\u003eWebcam\u003c/a\u003e\n\n---\n\n### Convolution kernel\n\n---\n\n```javascript\n// [-1,-1,-1,0,0,0,1,1,1] is a 3x3 kernel, where:\n// - 1st row is [-1,-1,-1]; 2nd row is [0,0,0]; 3rd row is [1,1,1].\nnumgl.convolution(storageId, [-1,-1,-1,0,0,0,1,1,1]);\n\n// Other kernels are supported.\n```\n\nJS Fiddle with it:\n\n- \u003ca href=\"http://jsfiddle.net/jongomez/6pgbwkff/\"\u003eImage\u003c/a\u003e\n- \u003ca href=\"http://jsfiddle.net/m1gbshz6/\"\u003eVideo\u003c/a\u003e\n- \u003ca href=\"https://jsfiddle.net/rsn9cdkb/1/\"\u003eWebcam\u003c/a\u003e\n\n---\n\n### Thresholding\n\n---\n\n```javascript\n// 80 is the thresholding value.\nnumgl.threshold(storageId, 80);\n```\n\nJS Fiddle:\n\n- \u003ca href=\"http://jsfiddle.net/jongomez/eap27rhq/\"\u003eImage\u003c/a\u003e\n- \u003ca href=\"http://jsfiddle.net/wuLcef2y/\"\u003eVideo\u003c/a\u003e\n- \u003ca href=\"https://jsfiddle.net/w8f7z6eu/\"\u003eWebcam\u003c/a\u003e\n\n---\n\n### Example - Combine convolution with threshold.\n\n---\n\n- The image processing functions return the GLSL variable that will hold the final pixel value.\n- This return variable can be used to chain call other image processing functions.\n\n```javascript\nwindow.onload = function() {\n\tvar imageId = numgl.store_picture(\"image\");\n\n\tnumgl.show_canvas(imageId);\n\t// Convolution followed by threshold\n\tvar convResult = numgl.convolution(imageId,[-1,-1,-1,0,0,0,1,1,1]);\n\tnumgl.threshold(convResult,10);\n\tnumgl.do_it();\n}\n```\n\nJS Fiddle:\n\n- \u003ca href=\"http://jsfiddle.net/b091mkbh/5/\"\u003eImage convolution + threshold\u003c/a\u003e\n\n---\n\n### Other options\n\n---\n\n- Enable / disable fps for video and webcam\n\n```javascript\n// If this is not set, the fps are not shown.\nnumgl.set_fps_element(fpsElementId);\n\n```\n\n- Store JS arrays as RGBA textures\n\n```javascript\n// Draws the following 2x2 RGBA texture: \n// [white, black,\n// black, white]\n\n// numgl.store_array()'s last 2 args are width and height.\nvar arrayId = numgl.store_array([255,255,255,255,\n\t\t\t\t\t\t\t\t0,0,0,255,\n\t\t\t\t\t\t\t\t0,0,0,255,\n\t\t\t\t\t\t\t\t255,255,255,255], \n\t\t\t\t\t\t\t\t2, 2);\n\n// show_texture() calls do_it() internally.\nnumgl.show_texture(arrayId);\n\n```\n\n- Read pixels from canvas\n\n```javascript\n// If no width and height are specified, read_canvas() will read the whole canvas.\nconsole.log(numgl.read_canvas().toString());\n\n// Using the above example the result would be: \"255,255,255,255,0,0,0,255,0,0,0,255,255,255,255,255\"\n\n```\n\n- Flip texture - arrays aren't flipped, but images and videos are by default (texture and clipspace coordinates are different). \u003ca href=\"http://jsfiddle.net/jongomez/n2gx6986/\"\u003eJS Fiddle example\u003c/a\u003e\n\n```javascript\n// Called before numgl.do_it()\nnumg.textures[storageId].flipTexture = true/false\n```\n\n- Manually define fl_FragCoord. \u003ca href=\"http://jsfiddle.net/jongomez/sbfe1yvz/\"\u003eJS Fiddle example\u003c/a\u003e\n\n```javascript\n// Paint the screen blue.\nnumgl.fragColor = \"vec4(0, 0, 1, 1);\"\n```\n\n- See the generated GLSL code.\n\n```javascript\n// Fragment shader code. Vertex shader code function is vs_code().\nconsole.log(numgl.fs_code(\"pretty\"));\n```\n\n---\n\n## How does it work\n\n- Video and webcam use requestAnimationFrame so the video/webcam frames are drawn multiple times. Pictures are only drawn once.\n\n- Javascript functions generate GLSL code, and when the user calls numgl.do_it() that GLSL code is compiled and executed. eg:\n\n```javascript\nwindow.onload = function() {\n\t// \"image\" is the \u003cimg\u003e tag ID.\n\tvar imageId = numgl.store_picture(\"image\");\n\n\tnumgl.grey(imageId);\n\t// Console log the GLSL code generated by the numgl.grey() call:\n\tconsole.log(numgl.fs_code(\"pretty\"));\n}\n```\n\nThe resulting GLSL code: (uTexture0 is the stored texture - it can be an array, picture or video frame)\n\n```GLSL\nprecision highp float;\nuniform vec2 uResolution;\nuniform sampler2D uTexture0;\nuniform vec2 uTextureSize0;\nvarying vec2 vTextCoords;\n\nvoid main(void) {\n\tfloat float_0 = 0.2126 * texture2D(uTexture0, vTextCoords).r;\n\tfloat float_1 = 0.7152 * texture2D(uTexture0, vTextCoords).g;\n\tfloat float_2 = 0.0722 * texture2D(uTexture0, vTextCoords).b;\n\tfloat float_3 = float_0 + float_2 + float_1;\n\tvec4 vec4_0 = vec4(float_3,float_3,float_3,1);\n\n\tgl_FragColor = vec4_0;\n }\n```\n\n- The final fragment color (gl_FragColor) is set by these GLSL code creating functions, and can be checked or set manually using numgl.fragColor. \u003ca href=\"http://jsfiddle.net/jongomez/sbfe1yvz/\"\u003eJS Fiddle example.\u003c/a\u003e\n\n---\n\n### Cross-origin / Same-origin resources\n\n---\n\n- For local images / video, you'll need to set up a local server. It's easy, just open up your console, go to your project's folder and type in ``` python3 -m http.server 8000 ```.\n\n- Images and video from other websites require the inline HTML attribute ``` crossorigin=\"anonymous\" ``` - \u003ca href=\"http://jsfiddle.net/jongomez/q3n5gj7u/\"\u003esee the img tag here, for example.\u003c/a\u003e\n\n---\n\n### Webcams on Chrome\n\n---\n\n- Webcams only work on https sites if you're using Chrome. On Firefox, webcams should work fine (for now).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjongomez%2Fnumgl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjongomez%2Fnumgl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjongomez%2Fnumgl/lists"}