{
  "source_file": "0930",
  "source_platform": "Hashnode",
  "author": "Lakshmi Mahabaleshwara",
  "title_listed": "How to Preprocess Medical Images for Machine Learning – A Guide Using Chest X-Rays",
  "keywords": "",
  "domain": "图像处理",
  "content_type": "深度技术解析",
  "is_paid_flag": "否",
  "url": "https://freecodecamp.org/news/how-to-preprocess-medical-images-for-machine-learning",
  "publish_date": "2026-06-04",
  "corpus_stage": "",
  "notes": "",
  "id": "c47a107a4461",
  "collected_at": "2026-10-02T14:12:00",
  "title": "How to Preprocess Medical Images for Machine Learning – A Guide Using Chest X-Rays",
  "body_markdown": "Working with healthcare data introduces preprocessing challenges that go beyond those you might encounter with structured data. Some familiar techniques still apply, while others look very different once your data becomes medical images.\nIn this article, you’ll learn how to prepare a real-world medical imaging dataset for machine learning, from initial data validation to a complete preprocessing pipeline.\nWe’ll use the Chest X-Ray Pneumonia dataset as our running example, but the lessons apply broadly to healthcare imaging data, including ultrasound, MRI, CT, and dermatology images.\nWhat You'll Learn in This Article\nBy the end of this article, you'll know how to:\nApproach healthcare data preprocessing differently from preprocessing structured data, and recognize where standard techniques fall short\nValidate a medical imaging dataset before training to catch corrupted files, mislabels, and data leakage between train and test\nApply six core preprocessing techniques for medical images\nBuild a complete preprocessing pipeline for chest X-rays using Python with OpenCV.\nWhat We'll Cover:\nWhy Preprocessing Data Matters More in Healthcare\nThe Dataset\nBefore Preprocessing: Validate the Dataset\nThe Six Pillars of Healthcare Imaging Preprocessing\nPillar 1: Scaling — Making the Numbers Play Fair\nPillar 2: Normalization — Centering the Data\nPillar 3: Guiding the Model's Attention\nPillar 4: Handling Missing Data\nPillar 5: Resizing & Resampling — Fitting Everything in the Same Frame\nPillar 6: Denoising & Artifact Handling — Cleaning the Window\nPutting it All together: A Complete Pipeline\nTry it Yourself\nConclusion\nWhy Preprocessing Data Matters More in Healthcare\nImagine handing a toddler a jigsaw puzzle with missing pieces, warped edges, and pieces from three different puzzles mixed together. The toddler can't solve it, but that isn't really the toddler's fault.\nThe same thing happens when raw, messy data gets fed into a machine learning model. A bad prediction on a clinical image can mean a missed diagnosis.\nHealthcare data tends to be messier than what most ML practitioners are used to:\nImages come from different machines, hospitals, and acquisition protocols\nLabels are inconsistent, sometimes missing, sometimes wrong\nPatient data is incomplete\nImage sizes, contrast levels, and orientations vary across sources\nPoor preprocessing often leads to models that perform well on benchmark datasets but struggle to generalize to data collected from different hospitals or imaging devices.\nThe Dataset\nThis guide uses the\nChest X-Ray Pneumonia dataset\nby Paul Mooney on Kaggle. It's a strong choice for learning preprocessing because:\nIt contains around 5,800 pediatric chest X-rays\nIt has two clear classes — Normal and Pneumonia\nIt's already organized into train, validation, and test folders\nThe images are recognizable without specialized medical training\nIt exhibits almost every preprocessing challenge worth learning\nThe dataset is available at\nKaggle: Chest X-Ray Pneumonia\n.\nFolder Structure\nAfter downloading, the dataset is organized like this:\nchest_xray/\n├── train/\n│   ├── NORMAL/\n│   └── PNEUMONIA/\n├── val/\n│   ├── NORMAL/\n│   └── PNEUMONIA/\n└── test/\n    ├── NORMAL/\n    └── PNEUMONIA/\nSide-by-side comparison — Normal vs Pneumonia chest X-ray:\nA quick first look at one of the images:\nimport os\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nimport cv2\n\nDATA_DIR = \"chest_xray\"\nTRAIN_DIR = os.path.join(DATA_DIR, \"train\")\n\n# Peek at a sample image\nsample_path = os.path.join(TRAIN_DIR, \"NORMAL\", os.listdir(os.path.join(TRAIN_DIR, \"NORMAL\"))[0])\nsample_image = cv2.imread(sample_path, cv2.IMREAD_GRAYSCALE)\n\nprint(f\"Image shape: {sample_image.shape}\")\nprint(f\"Pixel range: {sample_image.min()} to {sample_image.max()}\")\nprint(f\"Data type: {sample_image.dtype}\")\nThe output reveals a few useful things right away: most images are large (often around 1500×2000 pixels), pixel values fall in the 0–255 range, and image sizes vary across the dataset. Each of these observations will inform a preprocessing step.\nBefore Preprocessing: Validate the Dataset\nBefore applying any transformations, it's worth checking that the data itself is intact. This step alone catches issues that would otherwise cause training to fail silently or produce misleading results.\nA simple validation function:\ndef validate_dataset(data_dir):\n    \"\"\"Scan a dataset folder and flag common data quality issues.\"\"\"\n    corrupted = []\n    too_small = []\n    nearly_black = []\n    total = 0\n    \n    for class_name in os.listdir(data_dir):\n        class_path = os.path.join(data_dir, class_name)\n        if not os.path.isdir(class_path):\n            continue\n        for fname in os.listdir(class_path):\n            fpath = os.path.join(class_path, fname)\n            total += 1\n            try:\n                img = cv2.imread(fpath, cv2.IMREAD_GRAYSCALE)\n                if img is None:\n                    corrupted.append(fpath)\n                    continue\n                if img.shape[0] < 100 or img.shape[1] < 100:\n                    too_small.append(fpath)\n                if img.mean() < 5:\n                    nearly_black.append(fpath)\n            except Exception:\n                corrupted.append(fpath)\n    \n    print(f\"Total files scanned: {total}\")\n    print(f\"Corrupted: {len(corrupted)}\")\n    print(f\"Too small: {len(too_small)}\")\n    print(f\"Nearly black: {len(nearly_black)}\")\n    return corrupted, too_small, nearly_black\n\nvalidate_dataset(TRAIN_DIR)\nCommon issues this catches:\nCorrupted files\n— files that won't open at all\nEmpty or nearly-black images\n— failed acquisitions or saved-as-blank files\nWrong dimensions\n— thumbnails or partial downloads mixed in\nDuplicate images\n— the same scan appearing in both train and test (this causes data leakage)\nMislabeled images\n— a normal X-ray placed in the pneumonia folder\n⚠️ This step is critical\n, One corrupted file can crash a training loop hours into a run. One duplicate between train and test can inflate accuracy scores by several percentage points without anyone noticing.\nThe Six Pillars of Healthcare Imaging Preprocessing\nPreprocessing for medical images can be organized around six core concerns. Two of them carry over directly from preprocessing structured data. Two need to be adapted because the mechanics change when the input is an image. And two are entirely new, they only exist once the data becomes pictures of human bodies.\nPillar 1: Scaling — Making the Numbers Play Fair\nImagine two children comparing their collections. One has 3 seashells. The other has 3,000 stickers. Asking who has more makes the answer seem obvious, but the\nscales\nare completely different. Comparing them meaningfully means putting both collections on the same measuring system.\nIn medical images, pixels usually range from 0 to 255 in 8-bit images, or 0 to 65,535 in some 16-bit medical DICOM images. Neural networks tend to train faster and more reliably when input values are small numbers close to zero.\nThe fix:\nDivide every pixel by its maximum possible value, bringing everything into the 0-to-1 range.\nimage = cv2.imread(sample_path, cv2.IMREAD_GRAYSCALE)\n\n# Scale to [0, 1]\nimage_scaled = image.astype(np.float32) / 255.0\n\nprint(f\"Before scaling: {image.min()} to {image.max()}\")\nprint(f\"After scaling:  {image_scaled.min():.3f} to {image_scaled.max():.3f}\")\nTakeaway:\nPixel scaling follows the same principle as scaling any numerical feature. The values simply happen to be arranged as an image rather than a column.\nPillar 2: Normalization — Centering the Data\nImagine a teacher asks a class to rate a movie from 1 to 10. One child always gives 9s and 10s. Another spreads ratings evenly from 1 to 10. Comparing their opinions fairly requires adjusting each child's score relative to their own average.\nIn medical imaging even after scaling to 0–1, the overall brightness of images can vary. Some X-rays are taken with stronger exposure than others. Normalization shifts and rescales each image (or each channel) so the values are centered around zero with a standard deviation of one.\nThe fix:\nSubtract the mean, divide by the standard deviation.\n# Compute mean and std from the TRAINING set only — never from validation or test\ndef compute_train_stats(train_dir, sample_limit=1000):\n    \"\"\"Compute pixel mean and std across the training set.\"\"\"\n    pixel_values = []\n    count = 0\n    for class_name in os.listdir(train_dir):\n        class_path = os.path.join(train_dir, class_name)\n        for fname in os.listdir(class_path):\n            if count >= sample_limit:\n                break\n            img = cv2.imread(os.path.join(class_path, fname), cv2.IMREAD_GRAYSCALE)\n            if img is not None:\n                pixel_values.append(img.astype(np.float32).flatten() / 255.0)\n                count += 1\n    pixels = np.concatenate(pixel_values)\n    return pixels.mean(), pixels.std()\n\ntrain_mean, train_std = compute_train_stats(TRAIN_DIR)\nimage_normalized = (image_scaled - train_mean) / train_std\n⚠️\nAvoid this common mistake: Statistics for normalization should be computed from the training set only, never from validation or test. Including those in the calculation leaks information from the evaluation data into the model. The same statistics should then be applied to validation, test, and any new data at inference time.\nTakeaway:\nCentering and scaling each image around the dataset's statistics is the imaging equivalent of standardizing a feature column. The pixels are now comparable across images, regardless of how bright or dim each scan happened to be.\nPillar 3: Guiding the Model's Attention\nImagine a child walking into a crowded pet store. Instead of describing every animal in sight, a parent points to the features that matter:\n“Look at the soft fur, the fluffy tail, and the nice small size.”\nThe child learns where to focus their attention.\nMedical image preprocessing does something similar. It highlights the regions and features most relevant to the diagnostic task.\nRegion-of-interest (ROI) cropping\n— focus on the lung field and discard the patient's arms, machine borders, and any imprinted text\nContrast enhancement\n— use techniques like CLAHE (Contrast Limited Adaptive Histogram Equalization) to make subtle lung textures more visible\nChannel selection\n— for images stored as RGB but containing grayscale information, convert to single-channel input to reduce noise\nCLAHE applied to an X-ray:\n# CLAHE enhances local contrast — useful for X-rays\nclahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\nimage_enhanced = clahe.apply(image)\n\n# Visualize the difference\nfig, axes = plt.subplots(1, 2, figsize=(12, 6))\naxes[0].imshow(image, cmap='gray')\naxes[0].set_title('Original')\naxes[1].imshow(image_enhanced, cmap='gray')\naxes[1].set_title('After CLAHE')\nplt.show()\nTakeaway:\nThe goal of teaching the model what to look at hasn't changed. With structured data, the answer is in new columns. With images, the answer is in cropping, enhancement, and emphasizing the regions that carry diagnostic signal.\nPillar 4: Handling Missing Data\nImagine reading a storybook with a few damaged pages. You don’t throw away the entire book, you decide whether to skip the page, infer what might be missing, or mark it for review.\nIn medical imaging, missing data can mean corrupted files, missing labels, or incomplete studies rather than empty spreadsheet cells.\nThe same three strategies — drop, impute, flag — still apply, just with different mechanics:\n# Strategy 1: Drop — remove unreadable or empty images\ndef is_valid_image(path):\n    try:\n        img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)\n        if img is None:\n            return False\n        if img.mean() < 5:           # nearly black\n            return False\n        if img.shape[0] < 50 or img.shape[1] < 50:  # too small\n            return False\n        return True\n    except Exception:\n        return False\n\n# Strategy 2: Impute — rare for images, but possible (e.g., in painting to fill in missing patches). Generally avoided for diagnostic data.\n\n# Strategy 3: Flag — track which patients are missing which modalities,\n#   and let the model condition on availability. Common in multi-modal healthcare ML.\nTakeaway:\n\"Missing\" in imaging data is rarely just a NaN. It can be a broken file, an unlabeled scan, an absent modality, or a black corner inside an image. The same three strategies still apply.\nPillar 5: Resizing & Resampling — Fitting Everything in the Same Frame\nImagine displaying children’s drawings on a classroom wall. If every drawing is a different size, they won’t fit neatly into the display. You resize them while preserving their proportions.\nMedical images must often be resized to a common input size, but anatomical structures should retain their original shape.\nThe fix:\nResize all images to a common shape. For medical data,\nhow\nthe resizing is done matters.\nTARGET_SIZE = (224, 224)\n\n# Simple resize (may distort aspect ratio)\nimage_resized = cv2.resize(image, TARGET_SIZE)\n\n# Better: preserve aspect ratio with padding\ndef resize_with_padding(image, target_size):\n    h, w = image.shape[:2]\n    target_h, target_w = target_size\n    scale = min(target_h / h, target_w / w)\n    new_h, new_w = int(h * scale), int(w * scale)\n    resized = cv2.resize(image, (new_w, new_h))\n    \n    pad_h = target_h - new_h\n    pad_w = target_w - new_w\n    top, bottom = pad_h // 2, pad_h - pad_h // 2\n    left, right = pad_w // 2, pad_w - pad_w // 2\n    padded = cv2.copyMakeBorder(resized, top, bottom, left, right,\n                                 cv2.BORDER_CONSTANT, value=0)\n    return padded\n\nimage_clean_resize = resize_with_padding(image, TARGET_SIZE)\n⚠️ Why aspect ratio matters in healthcare:\nSquishing a chest X-ray horizontally makes the lungs look unnatural. Models trained on distorted anatomy often perform worse on real scans. Preserving aspect ratio is generally the safer choice.\nTakeaway:\nModels need a consistent input size, but the geometry of the anatomy needs to be preserved. Resize, but resize carefully.\nPillar 6: Denoising & Artifact Handling — Cleaning the Window\nImagine looking through a window with dust and smudges on the glass. Cleaning the window makes the view clearer, but scrubbing too aggressively could scratch the glass.\nSimilarly, medical images often contain noise and acquisition artifacts that should be reduced carefully without removing clinically important details.\nFor chest X-rays, the most common issues are mild noise and burned-in text or markers. A gentle median or bilateral filter helps with the first, while cropping or masking helps with the second.\n# Gentle denoising — careful not to blur away clinical detail\nimage_denoised = cv2.medianBlur(image, ksize=3)\n\n# Bilateral filter preserves edges better than a median filter\nimage_bilateral = cv2.bilateralFilter(image, d=5, sigmaColor=50, sigmaSpace=50)\n⚠️ A note of caution:\nAggressive denoising can erase the features a model needs to detect a disease. For diagnostic ML, gentle filtering is generally preferred. A useful rule of thumb: if a radiologist can't distinguish the cleaned image from the original, the filtering has gone too far.\nTakeaway:\nImaging data carries noise that structured data doesn't have. The window can be cleaned, but never so aggressively that the view is wiped away with the smudges.\nPutting it All Together: A Complete Pipeline\nHere's how the six pillars combine into a single preprocessing function for chest X-ray images:\ndef preprocess_xray(image_path, target_size=(224, 224),\n                    train_mean=0.482, train_std=0.236):\n    \"\"\"\n    Full preprocessing pipeline for chest X-ray images.\n    Applies all six pillars in order.\n    \"\"\"\n    # Pillar 4: Validate first — skip corrupted files\n    if not is_valid_image(image_path):\n        return None\n    \n    image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)\n    \n    # Pillar 5: Resize with aspect ratio preserved\n    image = resize_with_padding(image, target_size)\n    \n    # Pillar 6: Gentle denoising\n    image = cv2.medianBlur(image, 3)\n    \n    # Pillar 3: Enhance contrast to highlight lung texture\n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    image = clahe.apply(image)\n    \n    # Pillar 1: Scale to [0, 1]\n    image = image.astype(np.float32) / 255.0\n    \n    # Pillar 2: Normalize using training set statistics\n    image = (image - train_mean) / train_std\n    \n    return image\nTry it Yourself\nEvery code snippet in this article is bundled into a runnable Kaggle notebook:\nChest X-Ray Preprocessing — Kaggle Notebook\n. Fork it, attach the dataset, and run all the cells to see each preprocessing pillar in action on real chest X-rays.\nConclusion\nHere's a summary of what we've discussed in this article:\nPillar\nPurpose\nExample\nScaling\nStandardize pixel ranges\n0-255 → 0-1\nNormalization\nCenter brightness distributions\nz-score normalization\nAttention Guidance\nHighlight diagnostic regions\nCLAHE\nMissing Data Handling\nRemove unusable scans\nCorrupted files\nResizing\nConsistent input size\n224×224\nDenoising\nReduce acquisition noise\nMedian filter\nPreprocessing for structured data is about making numbers play fair so a model can see them clearly.\nPreprocessing for healthcare imaging is about respecting the messy reality of how medical data is captured, stored, and labeled. Some standard techniques carry over directly. Some need to be adapted. And a few preprocessing concerns only emerge once the data becomes pictures of human bodies.\nStepping back, whether it's a child learning to organize their toy box, or a model learning to spot pneumonia in a chest X-ray, the quality of learning depends on the quality of data preparation. Get the data right.\nIf this was useful, you can find a related conceptual primer on preprocessing more broadly here:\nData Preprocessing for Machine Learning\n.",
  "metadata": {
    "title": "How to Preprocess Medical Images for Machine Learning – A Guide Using Chest X-Rays",
    "description": "Working with healthcare data introduces preprocessing challenges that go beyond those you might encounter with structured data. Some familiar techniques still apply, while others look very different o",
    "keywords": null,
    "author": null,
    "og:site_name": "freeCodeCamp.org",
    "og:type": "article",
    "og:title": "How to Preprocess Medical Images for Machine Learning – A Guide Using Chest X-Rays",
    "og:description": "Working with healthcare data introduces preprocessing challenges that go beyond those you might encounter with structured data. Some familiar techniques still apply, while others look very different o",
    "og:url": "https://www.freecodecamp.org/news/how-to-preprocess-medical-images-for-machine-learning/",
    "og:image": "https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/eab58d7c-f63a-41ae-a01e-52a65b0be17c.png",
    "og:image:width": "1920",
    "og:image:height": "1080",
    "twitter:card": "summary_large_image",
    "twitter:title": "How to Preprocess Medical Images for Machine Learning – A Guide Using Chest X-Rays",
    "twitter:description": "Working with healthcare data introduces preprocessing challenges that go beyond those you might encounter with structured data. Some familiar techniques still apply, while others look very different o",
    "twitter:url": "https://www.freecodecamp.org/news/how-to-preprocess-medical-images-for-machine-learning/",
    "twitter:image": "https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/eab58d7c-f63a-41ae-a01e-52a65b0be17c.png",
    "twitter:label1": "Written by",
    "twitter:data1": "Lakshmi Mahabaleshwara",
    "twitter:label2": "Filed under",
    "twitter:data2": "Machine Learning, healthcare, Medical Imaging, data-engineering, Data Preprocessing, Python",
    "twitter:site": "@freecodecamp",
    "article:published_time": "2026-06-04T17:13:59.716Z",
    "article:modified_time": "2026-06-04T17:13:59.716Z",
    "article:tag": "Python",
    "article:publisher": "https://www.facebook.com/freecodecamp"
  },
  "paywall": {
    "listed_paid": false,
    "detected": false,
    "marker": "",
    "public_only": false,
    "policy": "付费/会员内容只保留页面公开可见部分，不绕过付费墙"
  },
  "crawl": {
    "success": true,
    "status_code": 200,
    "error": ""
  },
  "code_blocks": [
    {
      "file": "attachments/c47a107a4461/code/body-000.txt",
      "chars": 147,
      "language": "text"
    },
    {
      "file": "attachments/c47a107a4461/code/body-001.txt",
      "chars": 503,
      "language": "text"
    },
    {
      "file": "attachments/c47a107a4461/code/body-002.txt",
      "chars": 1159,
      "language": "text"
    },
    {
      "file": "attachments/c47a107a4461/code/body-003.txt",
      "chars": 258,
      "language": "text"
    },
    {
      "file": "attachments/c47a107a4461/code/body-004.txt",
      "chars": 859,
      "language": "text"
    },
    {
      "file": "attachments/c47a107a4461/code/body-005.txt",
      "chars": 377,
      "language": "text"
    },
    {
      "file": "attachments/c47a107a4461/code/body-006.txt",
      "chars": 721,
      "language": "text"
    },
    {
      "file": "attachments/c47a107a4461/code/body-007.txt",
      "chars": 784,
      "language": "text"
    },
    {
      "file": "attachments/c47a107a4461/code/body-008.txt",
      "chars": 254,
      "language": "text"
    },
    {
      "file": "attachments/c47a107a4461/code/body-009.txt",
      "chars": 949,
      "language": "text"
    }
  ],
  "images": [
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/55671e0b-95ea-4f99-b507-a8742e8981d9.png",
      "bytes": 50308,
      "reused": true,
      "local_path": "attachments/c47a107a4461/images/003.png",
      "src": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/55671e0b-95ea-4f99-b507-a8742e8981d9.png",
      "alt": "Illustration showing a healthcare data preprocessing workflow. Mixed medical images with different sizes, missing labels, noisy scans, and corrupted files enter a preprocessing pipeline and emerge as clean, standardized, model-ready images ready for machine learning.",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/b92e1e14-ac24-4314-afce-bc2c3ce3ea32.png",
      "bytes": 88023,
      "reused": true,
      "local_path": "attachments/c47a107a4461/images/004.png",
      "src": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/b92e1e14-ac24-4314-afce-bc2c3ce3ea32.png",
      "alt": "Side-by-side chest X-ray images showing a normal lung scan on the left and a pneumonia scan on the right. The pneumonia image contains visible cloudy opacities compared with the clearer lung fields in the normal image.",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/1d864b0d-992c-4637-8f43-7ca86c6fd93c.png",
      "bytes": 21935,
      "reused": true,
      "local_path": "attachments/c47a107a4461/images/005.png",
      "src": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/1d864b0d-992c-4637-8f43-7ca86c6fd93c.png",
      "alt": "Histogram comparison showing chest X-ray pixel values before and after scaling. The left histogram displays values in the 0–255 range, while the right histogram shows the same distribution scaled to the 0–1 range used for machine learning.",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/54cb1319-e794-472e-9ca4-22a063fd5092.png",
      "bytes": 43612,
      "reused": true,
      "local_path": "attachments/c47a107a4461/images/006.png",
      "src": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/54cb1319-e794-472e-9ca4-22a063fd5092.png",
      "alt": "Three-panel illustration showing a chest X-ray before and after feature enhancement. The first panel shows the original image, the second highlights the lung region of interest, and the third shows the image after CLAHE contrast enhancement with lung textures appearing more visible.",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/d36b6f8c-4be0-41b7-ab7c-5ca30c01b3e0.png",
      "bytes": 44539,
      "reused": true,
      "local_path": "attachments/c47a107a4461/images/007.png",
      "src": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/d36b6f8c-4be0-41b7-ab7c-5ca30c01b3e0.png",
      "alt": "Comparison of two chest X-ray resizing approaches. One image is stretched into a square shape, distorting the lungs, while the second preserves the original aspect ratio by adding padding around the image. The aspect-ratio-preserving approach is highlighted as the preferred method.",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/c532949b-000c-403e-acb9-f9dec689182e.png",
      "bytes": 35363,
      "reused": true,
      "local_path": "attachments/c47a107a4461/images/008.png",
      "src": "https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/c532949b-000c-403e-acb9-f9dec689182e.png",
      "alt": "Workflow showing a chest X-ray progressing through a healthcare imaging preprocessing pipeline. The image moves through validation, resizing, denoising, contrast enhancement, scaling, and normalization before becoming a model-ready machine learning input.",
      "downloaded": true
    }
  ],
  "audio": [],
  "videos": [],
  "links": [
    {
      "href": "https://www.kaggle.com/datasets/paultimothymooney/chest-xray-pneumonia",
      "label": "Kaggle: Chest X-Ray Pneumonia",
      "type": "a"
    },
    {
      "href": "https://www.kaggle.com/code/lakshmimahabaleshwar/chest-xray-preprocessing-kaggle",
      "label": "Chest X-Ray Preprocessing — Kaggle Notebook",
      "type": "a"
    },
    {
      "href": "https://lakshmimahabaleshwara.substack.com/p/data-preprocessing-for-machine-learning",
      "label": "Data Preprocessing for Machine Learning",
      "type": "a"
    }
  ],
  "repair_version": 2,
  "repaired_at": "2026-10-14T10:50:58Z",
  "source_url": "https://freecodecamp.org/news/how-to-preprocess-medical-images-for-machine-learning",
  "papers": [],
  "actual_video_files": [],
  "body_chars": 17847,
  "expected_body_images": 6,
  "discovery_status": "ok",
  "delivery_status": "ok",
  "missing_reason": "",
  "raw_html": "attachments/c47a107a4461/pages/source.html",
  "local_html": "attachments/c47a107a4461/pages/article.html",
  "body_base_path": "attachments/c47a107a4461/pages/",
  "body_markdown_path": "attachments/c47a107a4461/pages/article.md"
}