|
1 | 1 | {
|
2 | 2 | "cells": [
|
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "id": "45fd5ee6-9514-4fc2-8561-ec3aaec330a0", |
| 6 | + "metadata": {}, |
| 7 | + "source": [ |
| 8 | + "## Parallel Image thresholding using CUDA C++ and Python PIL" |
| 9 | + ] |
| 10 | + }, |
| 11 | + { |
| 12 | + "cell_type": "markdown", |
| 13 | + "id": "4947f8aa-ba7b-4755-9373-5bc23cc013fd", |
| 14 | + "metadata": {}, |
| 15 | + "source": [ |
| 16 | + "### We aim to threshold this image of CERN, to enhance focus on the Alps and white text" |
| 17 | + ] |
| 18 | + }, |
| 19 | + { |
| 20 | + "cell_type": "markdown", |
| 21 | + "id": "f1ac7a20-ddc1-408f-a42b-599f724c295b", |
| 22 | + "metadata": {}, |
| 23 | + "source": [ |
| 24 | + "<img src=\"img_in.jpg\" align=left width=\"400\">" |
| 25 | + ] |
| 26 | + }, |
3 | 27 | {
|
4 | 28 | "cell_type": "code",
|
5 | 29 | "execution_count": 1,
|
|
10 | 34 | "#include <iostream>\n",
|
11 | 35 | "#include <cmath>\n",
|
12 | 36 | "#include <cuda_runtime.h>\n",
|
13 |
| - "#include<vector>\n" |
| 37 | + "#include<vector>" |
| 38 | + ] |
| 39 | + }, |
| 40 | + { |
| 41 | + "cell_type": "markdown", |
| 42 | + "id": "482eb964-0d48-422d-8f67-fcd9fb785f89", |
| 43 | + "metadata": {}, |
| 44 | + "source": [ |
| 45 | + "### We define a CUDA kernel that brightens or underexposes pixels based on a threshold value:" |
14 | 46 | ]
|
15 | 47 | },
|
16 | 48 | {
|
|
65 | 97 | "name": "stdout",
|
66 | 98 | "output_type": "stream",
|
67 | 99 | "text": [
|
68 |
| - "Requirement already satisfied: Pillow in /opt/conda/envs/.venv/lib/python3.10/site-packages (10.0.1)\n" |
| 100 | + "Collecting Pillow\n", |
| 101 | + " Obtaining dependency information for Pillow from https://files.pythonhosted.org/packages/7a/07/e896b096a77375e78e02ce222ae4fd6014928cd76c691d312060a1645dfa/Pillow-10.0.1-cp310-cp310-manylinux_2_28_x86_64.whl.metadata\n", |
| 102 | + " Downloading Pillow-10.0.1-cp310-cp310-manylinux_2_28_x86_64.whl.metadata (9.5 kB)\n", |
| 103 | + "Downloading Pillow-10.0.1-cp310-cp310-manylinux_2_28_x86_64.whl (3.6 MB)\n", |
| 104 | + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.6/3.6 MB 2.2 MB/s eta 0:00:00\n", |
| 105 | + "Installing collected packages: Pillow\n", |
| 106 | + "Successfully installed Pillow-10.0.1\n" |
69 | 107 | ]
|
70 | 108 | }
|
71 | 109 | ],
|
|
83 | 121 | "name": "stdout",
|
84 | 122 | "output_type": "stream",
|
85 | 123 | "text": [
|
86 |
| - "Requirement already satisfied: numpy in /opt/conda/envs/.venv/lib/python3.10/site-packages (1.26.0)\n" |
| 124 | + "Collecting numpy\n", |
| 125 | + " Obtaining dependency information for numpy from https://files.pythonhosted.org/packages/9b/5a/f265a1ba3641d16b5480a217a6aed08cceef09cd173b568cd5351053472a/numpy-1.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", |
| 126 | + " Downloading numpy-1.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (58 kB)\n", |
| 127 | + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 58.5/58.5 kB 1.0 MB/s eta 0:00:00\n", |
| 128 | + "Downloading numpy-1.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (18.2 MB)\n", |
| 129 | + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 18.2/18.2 MB 2.6 MB/s eta 0:00:00\n", |
| 130 | + "Installing collected packages: numpy\n", |
| 131 | + "Successfully installed numpy-1.26.0\n" |
87 | 132 | ]
|
88 | 133 | }
|
89 | 134 | ],
|
|
101 | 146 | "name": "stdout",
|
102 | 147 | "output_type": "stream",
|
103 | 148 | "text": [
|
104 |
| - "Requirement already satisfied: matplotlib in /opt/conda/envs/.venv/lib/python3.10/site-packages (3.8.0)\n", |
105 |
| - "Requirement already satisfied: contourpy>=1.0.1 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from matplotlib) (1.1.1)\n", |
106 |
| - "Requirement already satisfied: cycler>=0.10 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from matplotlib) (0.11.0)\n", |
107 |
| - "Requirement already satisfied: fonttools>=4.22.0 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from matplotlib) (4.42.1)\n", |
108 |
| - "Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from matplotlib) (1.4.5)\n", |
| 149 | + "Collecting matplotlib\n", |
| 150 | + " Obtaining dependency information for matplotlib from https://files.pythonhosted.org/packages/b5/24/aaccf324ce862bb82277e8814d2aebbb2a2c160d04e95aa2b8c9dc3137a9/matplotlib-3.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", |
| 151 | + " Downloading matplotlib-3.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (5.8 kB)\n", |
| 152 | + "Collecting contourpy>=1.0.1 (from matplotlib)\n", |
| 153 | + " Obtaining dependency information for contourpy>=1.0.1 from https://files.pythonhosted.org/packages/f1/6b/e4b0f8708f22dd7c321f87eadbb98708975e115ac6582eb46d1f32197ce6/contourpy-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", |
| 154 | + " Downloading contourpy-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (5.9 kB)\n", |
| 155 | + "Collecting cycler>=0.10 (from matplotlib)\n", |
| 156 | + " Obtaining dependency information for cycler>=0.10 from https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl.metadata\n", |
| 157 | + " Downloading cycler-0.12.1-py3-none-any.whl.metadata (3.8 kB)\n", |
| 158 | + "Collecting fonttools>=4.22.0 (from matplotlib)\n", |
| 159 | + " Obtaining dependency information for fonttools>=4.22.0 from https://files.pythonhosted.org/packages/ac/ed/9a33eca5e2cc35dc1fea0a968509c653db9a99a5979656ae57c6c019d66b/fonttools-4.43.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata\n", |
| 160 | + " Downloading fonttools-4.43.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (152 kB)\n", |
| 161 | + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 152.4/152.4 kB 1.4 MB/s eta 0:00:00\n", |
| 162 | + "Collecting kiwisolver>=1.0.1 (from matplotlib)\n", |
| 163 | + " Obtaining dependency information for kiwisolver>=1.0.1 from https://files.pythonhosted.org/packages/6f/40/4ab1fdb57fced80ce5903f04ae1aed7c1d5939dda4fd0c0aa526c12fe28a/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl.metadata\n", |
| 164 | + " Downloading kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl.metadata (6.4 kB)\n", |
109 | 165 | "Requirement already satisfied: numpy<2,>=1.21 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from matplotlib) (1.26.0)\n",
|
110 |
| - "Requirement already satisfied: packaging>=20.0 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from matplotlib) (23.1)\n", |
| 166 | + "Requirement already satisfied: packaging>=20.0 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from matplotlib) (23.2)\n", |
111 | 167 | "Requirement already satisfied: pillow>=6.2.0 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from matplotlib) (10.0.1)\n",
|
112 |
| - "Requirement already satisfied: pyparsing>=2.3.1 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from matplotlib) (3.1.1)\n", |
| 168 | + "Collecting pyparsing>=2.3.1 (from matplotlib)\n", |
| 169 | + " Obtaining dependency information for pyparsing>=2.3.1 from https://files.pythonhosted.org/packages/39/92/8486ede85fcc088f1b3dba4ce92dd29d126fd96b0008ea213167940a2475/pyparsing-3.1.1-py3-none-any.whl.metadata\n", |
| 170 | + " Downloading pyparsing-3.1.1-py3-none-any.whl.metadata (5.1 kB)\n", |
113 | 171 | "Requirement already satisfied: python-dateutil>=2.7 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from matplotlib) (2.8.2)\n",
|
114 |
| - "Requirement already satisfied: six>=1.5 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n" |
| 172 | + "Requirement already satisfied: six>=1.5 in /opt/conda/envs/.venv/lib/python3.10/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n", |
| 173 | + "Downloading matplotlib-3.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (11.6 MB)\n", |
| 174 | + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 11.6/11.6 MB 1.8 MB/s eta 0:00:00\n", |
| 175 | + "Downloading contourpy-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (301 kB)\n", |
| 176 | + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 301.7/301.7 kB 1.8 MB/s eta 0:00:00\n", |
| 177 | + "Downloading cycler-0.12.1-py3-none-any.whl (8.3 kB)\n", |
| 178 | + "Downloading fonttools-4.43.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.5 MB)\n", |
| 179 | + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.5/4.5 MB 2.4 MB/s eta 0:00:00\n", |
| 180 | + "Downloading kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (1.6 MB)\n", |
| 181 | + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.6/1.6 MB 2.7 MB/s eta 0:00:00\n", |
| 182 | + "Downloading pyparsing-3.1.1-py3-none-any.whl (103 kB)\n", |
| 183 | + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 103.1/103.1 kB 3.4 MB/s eta 0:00:00\n", |
| 184 | + "Installing collected packages: pyparsing, kiwisolver, fonttools, cycler, contourpy, matplotlib\n", |
| 185 | + "Successfully installed contourpy-1.1.1 cycler-0.12.1 fonttools-4.43.1 kiwisolver-1.4.5 matplotlib-3.8.0 pyparsing-3.1.1\n" |
115 | 186 | ]
|
116 | 187 | }
|
117 | 188 | ],
|
118 | 189 | "source": [
|
119 | 190 | "!pip install matplotlib"
|
120 | 191 | ]
|
121 | 192 | },
|
| 193 | + { |
| 194 | + "cell_type": "markdown", |
| 195 | + "id": "6d6a53e4-c2a1-49ad-9f76-90a86bf022b8", |
| 196 | + "metadata": {}, |
| 197 | + "source": [ |
| 198 | + "### Read the image in Python using PIL and convert to grayscale" |
| 199 | + ] |
| 200 | + }, |
122 | 201 | {
|
123 | 202 | "cell_type": "code",
|
124 | 203 | "execution_count": 8,
|
|
143 | 222 | {
|
144 | 223 | "cell_type": "code",
|
145 | 224 | "execution_count": 9,
|
146 |
| - "id": "6a1cd914-db44-4e34-a9fb-5d2100b298b8", |
147 |
| - "metadata": {}, |
148 |
| - "outputs": [ |
149 |
| - { |
150 |
| - "name": "stdout", |
151 |
| - "output_type": "stream", |
152 |
| - "text": [ |
153 |
| - "[[126 126 125 ... 84 84 84]\n", |
154 |
| - " [126 126 125 ... 84 84 84]\n", |
155 |
| - " [127 126 126 ... 84 84 84]\n", |
156 |
| - " ...\n", |
157 |
| - " [ 58 55 63 ... 19 15 8]\n", |
158 |
| - " [ 57 56 53 ... 23 17 16]\n", |
159 |
| - " [ 55 50 58 ... 5 16 16]]\n" |
160 |
| - ] |
161 |
| - } |
162 |
| - ], |
163 |
| - "source": [ |
164 |
| - "%%python\n", |
165 |
| - "\n", |
166 |
| - "print(image_array)" |
167 |
| - ] |
168 |
| - }, |
169 |
| - { |
170 |
| - "cell_type": "code", |
171 |
| - "execution_count": 10, |
172 | 225 | "id": "7545b5c9-c901-4599-abde-3ae5ac17e160",
|
173 | 226 | "metadata": {},
|
174 | 227 | "outputs": [],
|
|
186 | 239 | "}"
|
187 | 240 | ]
|
188 | 241 | },
|
| 242 | + { |
| 243 | + "cell_type": "markdown", |
| 244 | + "id": "6eb60938-dbd8-49f4-8e3b-23a975f36143", |
| 245 | + "metadata": {}, |
| 246 | + "source": [ |
| 247 | + "### Converting the python image into a std::vector using Cppyy:" |
| 248 | + ] |
| 249 | + }, |
189 | 250 | {
|
190 | 251 | "cell_type": "code",
|
191 |
| - "execution_count": 11, |
| 252 | + "execution_count": 10, |
192 | 253 | "id": "a3c74a47-399e-4454-89f6-f4e8b0376a68",
|
193 | 254 | "metadata": {},
|
194 | 255 | "outputs": [],
|
|
201 | 262 | "img_vector = cppyy.gbl.std.vector['float'](img_list)"
|
202 | 263 | ]
|
203 | 264 | },
|
| 265 | + { |
| 266 | + "cell_type": "markdown", |
| 267 | + "id": "cd6d8d32-6175-4af3-a0eb-7d563e142f1e", |
| 268 | + "metadata": {}, |
| 269 | + "source": [ |
| 270 | + "### C++ function that recives the std::vector and passes it to the CUDA function host input:" |
| 271 | + ] |
| 272 | + }, |
204 | 273 | {
|
205 | 274 | "cell_type": "code",
|
206 |
| - "execution_count": 12, |
| 275 | + "execution_count": 11, |
207 | 276 | "id": "ab6c16bd-c44a-4e4e-985a-12c5ca3256a8",
|
208 | 277 | "metadata": {},
|
209 | 278 | "outputs": [],
|
|
222 | 291 | "}\n"
|
223 | 292 | ]
|
224 | 293 | },
|
| 294 | + { |
| 295 | + "cell_type": "markdown", |
| 296 | + "id": "75a21ae2-cffa-4f34-9404-c59577d9b67f", |
| 297 | + "metadata": {}, |
| 298 | + "source": [ |
| 299 | + "### C++ function to return the host output to the Python side:" |
| 300 | + ] |
| 301 | + }, |
225 | 302 | {
|
226 | 303 | "cell_type": "code",
|
227 |
| - "execution_count": 13, |
| 304 | + "execution_count": 12, |
228 | 305 | "id": "a168bbab-fc67-41f5-85c1-f9980d75a00b",
|
229 | 306 | "metadata": {},
|
230 | 307 | "outputs": [],
|
|
240 | 317 | },
|
241 | 318 | {
|
242 | 319 | "cell_type": "code",
|
243 |
| - "execution_count": 14, |
| 320 | + "execution_count": 13, |
244 | 321 | "id": "2eec8b62-5500-45e4-9c02-9bc587288206",
|
245 | 322 | "metadata": {},
|
246 | 323 | "outputs": [],
|
|
252 | 329 | },
|
253 | 330 | {
|
254 | 331 | "cell_type": "code",
|
255 |
| - "execution_count": 15, |
| 332 | + "execution_count": 14, |
256 | 333 | "id": "fd1bef06-d14e-4c82-8fa5-da14df696fe5",
|
257 | 334 | "metadata": {},
|
258 | 335 | "outputs": [
|
|
268 | 345 | "displayImgArray(h_input);"
|
269 | 346 | ]
|
270 | 347 | },
|
| 348 | + { |
| 349 | + "cell_type": "markdown", |
| 350 | + "id": "fa4fb49f-ea0f-40c0-bf4d-92ad469e0426", |
| 351 | + "metadata": {}, |
| 352 | + "source": [ |
| 353 | + "### Allocating CUDA memory and calling the kernel" |
| 354 | + ] |
| 355 | + }, |
271 | 356 | {
|
272 | 357 | "cell_type": "code",
|
273 |
| - "execution_count": 16, |
| 358 | + "execution_count": 15, |
274 | 359 | "id": "ea90464f-ed8f-44f1-9336-99aa3dc85149",
|
275 | 360 | "metadata": {},
|
276 | 361 | "outputs": [],
|
|
294 | 379 | "cudaFree(d_output)"
|
295 | 380 | ]
|
296 | 381 | },
|
| 382 | + { |
| 383 | + "cell_type": "markdown", |
| 384 | + "id": "1edf472e-fe51-4fd1-a0e5-e30a2d092739", |
| 385 | + "metadata": {}, |
| 386 | + "source": [ |
| 387 | + "### Verifying host output" |
| 388 | + ] |
| 389 | + }, |
297 | 390 | {
|
298 | 391 | "cell_type": "code",
|
299 |
| - "execution_count": 17, |
| 392 | + "execution_count": 16, |
300 | 393 | "id": "7bf09ac1-ac6b-45e9-93c2-d4ae50897c2d",
|
301 | 394 | "metadata": {},
|
302 | 395 | "outputs": [
|
|
314 | 407 | },
|
315 | 408 | {
|
316 | 409 | "cell_type": "code",
|
317 |
| - "execution_count": 18, |
| 410 | + "execution_count": 17, |
318 | 411 | "id": "3a97a53f-c2b7-4e24-a299-a6c0ec81b16e",
|
319 | 412 | "metadata": {},
|
320 | 413 | "outputs": [],
|
321 | 414 | "source": [
|
322 | 415 | "std::vector<float> blurredRes = getOutput();"
|
323 | 416 | ]
|
324 | 417 | },
|
| 418 | + { |
| 419 | + "cell_type": "markdown", |
| 420 | + "id": "e22c59e0-019f-479f-8fa0-63e00b680702", |
| 421 | + "metadata": {}, |
| 422 | + "source": [ |
| 423 | + "### Access the CUDA filter result on the Python side:" |
| 424 | + ] |
| 425 | + }, |
325 | 426 | {
|
326 | 427 | "cell_type": "code",
|
327 |
| - "execution_count": 19, |
| 428 | + "execution_count": 18, |
328 | 429 | "id": "2586f437-295d-4b33-b18e-6f28628a7f81",
|
329 | 430 | "metadata": {},
|
330 | 431 | "outputs": [],
|
|
337 | 438 | },
|
338 | 439 | {
|
339 | 440 | "cell_type": "code",
|
340 |
| - "execution_count": 20, |
| 441 | + "execution_count": 19, |
341 | 442 | "id": "a8104074-7398-4653-a81a-827dd81a16b3",
|
342 | 443 | "metadata": {},
|
343 | 444 | "outputs": [],
|
|
352 | 453 | "id": "d0d2e15b-5a5f-4ed8-8f17-3fd1f3fc4daf",
|
353 | 454 | "metadata": {},
|
354 | 455 | "source": [
|
355 |
| - "<img src=\"img_out.jpg\" align=right width=\"400\">\n", |
356 |
| - "<img src=\"img_in.jpg\" align=left width=\"400\">" |
| 456 | + "<img src=\"img_out.jpg\" align=left width=\"400\">\n" |
357 | 457 | ]
|
| 458 | + }, |
| 459 | + { |
| 460 | + "cell_type": "code", |
| 461 | + "execution_count": null, |
| 462 | + "id": "0969406d-0bab-4022-bbeb-0e2581035ef4", |
| 463 | + "metadata": {}, |
| 464 | + "outputs": [], |
| 465 | + "source": [] |
358 | 466 | }
|
359 | 467 | ],
|
360 | 468 | "metadata": {
|
361 | 469 | "kernelspec": {
|
362 |
| - "display_name": "CUDA (C++17)", |
| 470 | + "display_name": "CppInterOp (C++17)", |
363 | 471 | "language": "CUDA",
|
364 |
| - "name": "cuda-xcpp17" |
| 472 | + "name": "cppinterop-xcpp17" |
365 | 473 | },
|
366 | 474 | "language_info": {
|
367 | 475 | "codemirror_mode": "text/x-c++src",
|
|
0 commit comments