{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.10","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":29653,"databundleVersionId":2420395,"sourceType":"competition"},{"sourceId":76071118,"sourceType":"kernelVersion"}],"dockerImageVersionId":30120,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# Tham khảo từ https://www.kaggle.com/code/davidbroberts/manual-voi-lut-on-mr-images\n# Module OS trong Python cung cấp nhiều phương thức hữu ích để quản lý file và folder, \n# giúp tương tác với hệ điều hành dễ dàng hơn. Dưới đây là một số phương thức quan trọng:\n# tham khảo import os o trang https://viettuts.vn/python/mo-dun-os-trong-python\n# tham khảo import os o trang https://pythonve.ikitai.net/entry/2024/01/08/013800#google_vignette\nimport os\n#-----------------------------------------------------------------------------------\n# Numpy là một thư viện lõi phục vụ cho khoa học máy tính của Python, hỗ trợ cho việc \n# tính toán các mảng nhiều chiều, có kích thước lớn với các hàm đã được tối ưu áp dụng\n# lên các mảng nhiều chiều đó. Numpy đặc biệt hữu ích khi thực hiện các hàm liên quan \n# tới Đại Số Tuyến Tính.\n# tham khảo trang https://viblo.asia/p/\n#   gioi-thieu-ve-numpy-mot-thu-vien-chu-yeu-phuc-vu-cho-khoa-hoc-may-tinh-cua-python-maGK7kz9Kj2\nimport numpy as np\n#-----------------------------------------------------------------------------------\nimport pandas as pd\n#-----------------------------------------------------------------------------------\nimport pydicom\n#-----------------------------------------------------------------------------------\nimport matplotlib.pyplot as plt\n#-----------------------------------------------------------------------------------","metadata":{"execution":{"iopub.status.busy":"2025-03-16T06:19:15.450758Z","iopub.execute_input":"2025-03-16T06:19:15.451181Z","iopub.status.idle":"2025-03-16T06:19:15.456139Z","shell.execute_reply.started":"2025-03-16T06:19:15.451147Z","shell.execute_reply":"2025-03-16T06:19:15.455169Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 8-neighboars - định nghĩa về lân cận của một điểm ảnh\nx=0\ny=0\narr2 = np.array(([(x-1,y-1,x,y-1,x+1,y-1), (x-1,y-1,x-1,y,x-1,y+1)],\n                 [(x-1,y,x,y,x+1,y), (x,y-1,x,y,x,y+1)],\n                 [(x-1,y+1,x,y+1,x+1,y+1), (x+1,y-1,x+1,y,x+1,y+1)]), dtype = int)\n\nprint(arr2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-16T06:34:26.402725Z","iopub.execute_input":"2025-03-16T06:34:26.403108Z","iopub.status.idle":"2025-03-16T06:34:26.410755Z","shell.execute_reply.started":"2025-03-16T06:34:26.403077Z","shell.execute_reply":"2025-03-16T06:34:26.409690Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Hàm này cung cấp tên của Module os được import.\n# Hiện tại, nó đăng ký 'posix', 'nt', 'os2', 'ce', 'java' và 'riscos'. \n# Ví dụ: posix của kaggle.com\nprint(os.name)\n# Lấy folder làm việc hiện tại (output là /kaggle/working )\ncurrent_dir = os.getcwd()\nprint(f'folder làm việc hiện tại: {current_dir}')\n# Liệt kê các file trong folder\nfiles = os.listdir('.')\nprint('Danh sách file trong folder hiện tại:', files)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-16T06:13:43.908686Z","iopub.execute_input":"2025-03-16T06:13:43.909054Z","iopub.status.idle":"2025-03-16T06:13:43.915792Z","shell.execute_reply.started":"2025-03-16T06:13:43.909017Z","shell.execute_reply":"2025-03-16T06:13:43.914778Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Make a simple linear VOI LUT from the raw (stored) pixel data\ndef make_lut(pixels, width, center, p_i):\n    \n    # Slope and Intercept set to 1 and 0 for MR. Get these from DICOM tags instead if using \n    # on a modality that requires them (CT, PT etc)\n    slope = 1.0\n    intercept = 0.0\n    min_pixel = int(np.amin(pixels))\n    max_pixel = int(np.amax(pixels))\n\n    # Make an empty array for the LUT the size of the pixel 'width' in the raw pixel data\n    lut = [0] * (max_pixel + 1)\n    \n    # Invert pixels and cent for MONOCHROME1. We invert the specified center so that \n    # increasing the center value makes the images brighter regardless of photometric intrepretation\n    invert = False\n    if p_i == \"MONOCHROME1\":\n        invert = True\n    else:\n        center = (max_pixel - min_pixel) - center\n        \n    # Loop through the pixels and calculate each LUT value\n    for pix_value in range(min_pixel, max_pixel):\n        lut_value = pix_value * slope + intercept\n        voi_value = (((lut_value - center) /  width + 0.5) * 255.0)\n        clamped_value = min(max(voi_value, 0), 255)\n        if invert:\n            lut[pix_value] = round(255 - clamped_value)\n        else:\n            lut[pix_value] = round(clamped_value)\n        \n    return lut","metadata":{"execution":{"iopub.status.busy":"2025-03-16T06:09:02.204265Z","iopub.execute_input":"2025-03-16T06:09:02.204572Z","iopub.status.idle":"2025-03-16T06:09:02.217199Z","shell.execute_reply.started":"2025-03-16T06:09:02.204540Z","shell.execute_reply":"2025-03-16T06:09:02.216159Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Apply the LUT to a pixel array\ndef apply_lut(pixels_in, lut):\n    \n    pixels_in = pixels_in.flatten()\n    pixels_out = [0] * len(pixels_in)\n    \n    for i in range(0, len(pixels_in)):\n        pixel = pixels_in[i]\n        pixels_out[i] = int(lut[pixel])\n        \n    return pixels_out","metadata":{"execution":{"iopub.status.busy":"2025-03-16T06:09:02.218685Z","iopub.execute_input":"2025-03-16T06:09:02.219116Z","iopub.status.idle":"2025-03-16T06:09:02.238119Z","shell.execute_reply.started":"2025-03-16T06:09:02.219059Z","shell.execute_reply":"2025-03-16T06:09:02.237103Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### - Load and display an image","metadata":{}},{"cell_type":"code","source":"from pydicom.pixel_data_handlers.util import apply_voi_lut\n# Load an image\nimage = pydicom.dcmread('../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00148/T1wCE/Image-95.dcm')\n\npixels = image.pixel_array\n\n# Print out the pixel 'width'\nprint(\"Min pixel value: \" + str(np.min(pixels)))\nprint(\"Max pixel value: \" + str(np.max(pixels)))\n\nprint(image.PhotometricInterpretation)\n\n\nplt.figure(figsize= (6,6))\nplt.imshow(pixels, cmap='gray');","metadata":{"execution":{"iopub.status.busy":"2025-03-16T06:09:02.239528Z","iopub.execute_input":"2025-03-16T06:09:02.239954Z","iopub.status.idle":"2025-03-16T06:09:02.456209Z","shell.execute_reply.started":"2025-03-16T06:09:02.239918Z","shell.execute_reply":"2025-03-16T06:09:02.455111Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot a histogram of the raw pixel data\nfig, axes = plt.subplots(nrows=1, ncols=1,sharex=False, sharey=False, figsize=(10,4))\nplt.title('Pixel Range: ' + str(np.min(pixels)) + '-' + str(np.max(pixels)))\nplt.hist(pixels.ravel(), np.max(pixels), (1, np.max(pixels)))\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2025-03-16T06:09:02.459447Z","iopub.execute_input":"2025-03-16T06:09:02.459924Z","iopub.status.idle":"2025-03-16T06:09:03.840556Z","shell.execute_reply.started":"2025-03-16T06:09:02.459887Z","shell.execute_reply":"2025-03-16T06:09:03.839077Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- The pixel range or width, is from 0 to 2857. That's 2858 shades of gray.\n- Since we can't display such a 'wide' image on our consumer grade display adapters and monitors, we need to bin the pixels to a usable range (8 bit).\n\n### - Make a LUT\n- If we specify **window_center = window_width / 2** as the center, the image will appear as the default image above does.\n- Lowering the width has the effect of *decreasing the contrast* by binning pixels.\n- Lowering the center has the effect of *decreasing the brightness*.\n\n### - Now we'll tweak the window values to produce a more contrasty image.","metadata":{}},{"cell_type":"code","source":"# Apply three different WW/WL settings via LUT. We'll set the center slightly less than half to adjust for brightness.\nwindow_width_1 = np.max(image.pixel_array)\nwindow_center_1 = window_width_1 / 2\n\nlut = make_lut(image.pixel_array, window_width_1, window_center_1, image.PhotometricInterpretation)\nimage1 = np.reshape(apply_lut(pixels, lut), (pixels.shape[0],pixels.shape[1]))\n\nwindow_width_2 = 450\nwindow_center_2 = 450\n\nlut = make_lut(image.pixel_array, window_width_2, window_center_2, image.PhotometricInterpretation)\nimage2 = np.reshape(apply_lut(pixels, lut), (pixels.shape[0],pixels.shape[1]))\n\nwindow_width_3 = 900\nwindow_center_3 = 90\n\nlut = make_lut(image.pixel_array, window_width_3, window_center_3, image.PhotometricInterpretation)\nimage3 = np.reshape(apply_lut(pixels, lut), (pixels.shape[0],pixels.shape[1]))","metadata":{"execution":{"iopub.status.busy":"2025-03-16T06:09:03.842574Z","iopub.execute_input":"2025-03-16T06:09:03.843091Z","iopub.status.idle":"2025-03-16T06:09:04.187101Z","shell.execute_reply.started":"2025-03-16T06:09:03.843036Z","shell.execute_reply":"2025-03-16T06:09:04.185758Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, axes = plt.subplots(nrows=2, ncols=2,sharex=True, sharey=True, figsize=(12, 12))\nax = axes.ravel()\nax[0].set_title('Default Image')\nax[0].imshow(image.pixel_array, cmap='gray')\nax[1].set_title(f'Width: {window_width_1} / Center: {window_center_1}')\nax[1].imshow(image1, cmap='gray')\nax[2].set_title(f'Width: {window_width_2} / Center: {window_center_2}')\nax[2].imshow(image2, cmap='gray')\nax[3].set_title(f'Width: {window_width_3} / Center: {window_center_3}')\nax[3].imshow(image3, cmap='gray')\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2025-03-16T06:09:04.189188Z","iopub.execute_input":"2025-03-16T06:09:04.189641Z","iopub.status.idle":"2025-03-16T06:09:05.009768Z","shell.execute_reply.started":"2025-03-16T06:09:04.189569Z","shell.execute_reply":"2025-03-16T06:09:05.008638Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### - Result: \n#### - The tumor area is more contrasted against the surrounding brain tissue. Especially in the bottom left image.\n#### - There are fine details visible in this image that are obscured on the other images.\n\n### - Let's load another image and try the same process.\n- Just copy the last 3 cells and change the filename.","metadata":{}},{"cell_type":"code","source":"# Load an image\nimage = pydicom.dcmread('../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00014/FLAIR/Image-126.dcm')\npixels = image.pixel_array\n\n# Print out the pixel 'width'\nprint(\"Min pixel value: \" + str(np.min(pixels)))\nprint(\"Max pixel value: \" + str(np.max(pixels)))\n\nplt.figure(figsize= (6,6))\nplt.imshow(pixels, cmap='gray');","metadata":{"execution":{"iopub.status.busy":"2025-03-16T06:09:05.011155Z","iopub.execute_input":"2025-03-16T06:09:05.011472Z","iopub.status.idle":"2025-03-16T06:09:05.218747Z","shell.execute_reply.started":"2025-03-16T06:09:05.011440Z","shell.execute_reply":"2025-03-16T06:09:05.217392Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### - This image doesn't have as wide of pixel range as the first image does. \n- We'll set it's level a little bit lower and try to make the tumor stand out better.","metadata":{}},{"cell_type":"code","source":"# Apply three different WW/WL settings via LUT. We'll set the level slightly less than half to adjust for brightness.\nwindow_width_1 = 1000\nwindow_center_1 = 900\n\nlut = make_lut(image.pixel_array, window_width_1, window_center_1, image.PhotometricInterpretation)\nimage1 = np.reshape(apply_lut(pixels, lut), (pixels.shape[0],pixels.shape[1]))\n\nwindow_width_2 = 600\nwindow_width_2 = 900\n\nlut = make_lut(image.pixel_array, window_width_2, window_center_2, image.PhotometricInterpretation)\nimage2 = np.reshape(apply_lut(pixels, lut), (pixels.shape[0],pixels.shape[1]))\n\nwindow_width_3 = 300\nwindow_center_3 = 900\n\nlut = make_lut(image.pixel_array, window_width_3, window_center_3, image.PhotometricInterpretation)\nimage3 = np.reshape(apply_lut(pixels, lut), (pixels.shape[0],pixels.shape[1]))","metadata":{"execution":{"iopub.status.busy":"2025-03-16T06:09:05.220332Z","iopub.execute_input":"2025-03-16T06:09:05.220674Z","iopub.status.idle":"2025-03-16T06:09:05.559484Z","shell.execute_reply.started":"2025-03-16T06:09:05.220639Z","shell.execute_reply":"2025-03-16T06:09:05.558343Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, axes = plt.subplots(nrows=2, ncols=2,sharex=True, sharey=True, figsize=(12, 12))\nax = axes.ravel()\nax[0].set_title('Default Image')\nax[0].imshow(image.pixel_array, cmap='gray')\nax[1].set_title(f'Width: {window_width_1} / Center: {window_center_1}')\nax[1].imshow(image1, cmap='gray')\nax[2].set_title(f'Width: {window_width_2} / Center: {window_center_2}')\nax[2].imshow(image2, cmap='gray')\nax[3].set_title(f'Width: {window_width_3} / Center: {window_center_3}')\nax[3].imshow(image3, cmap='gray')\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2025-03-16T06:09:05.560738Z","iopub.execute_input":"2025-03-16T06:09:05.561030Z","iopub.status.idle":"2025-03-16T06:09:06.300796Z","shell.execute_reply.started":"2025-03-16T06:09:05.561001Z","shell.execute_reply":"2025-03-16T06:09:06.299737Z"},"trusted":true},"outputs":[],"execution_count":null}]}