<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Data Preprocessing - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Data Preprocessing - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 23 Oct 2026 15:43:50 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/data-preprocessing/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ What Happens to a Medical Image Before and After a Model Sees It ]]>
                </title>
                <description>
                    <![CDATA[ Medical imaging papers are full of familiar-looking terms: normalization, labels, validation, annotation, and preprocessing. If you come from general machine learning, you may think you already know w ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-happens-to-a-medical-image-before-and-after-a-model-sees-it/</link>
                <guid isPermaLink="false">6a8cac4b80ffa6e1b9da795b</guid>
                
                    <category>
                        <![CDATA[ Medical Imaging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Healthcare AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #medical-ai ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Data Preprocessing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Image Segmentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ pytorch ]]>
                    </category>
                
                    <category>
                        <![CDATA[ De-identification ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI in Healthcare,  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lakshmi Mahabaleshwara ]]>
                </dc:creator>
                <pubDate>Mon, 24 Aug 2026 20:40:43 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/9014bf41-16ea-4661-926a-4444a53484f8.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Medical imaging papers are full of familiar-looking terms: normalization, labels, validation, annotation, and preprocessing.</p>
<p>If you come from general machine learning, you may think you already know what these words mean. And sometimes you do.</p>
<p>But medical imaging adds a few twists. Some terms have a different meaning, and some are used in more than one way depending on the context.</p>
<p>This article follows a chest X-ray from the moment it's acquired to the point where a model makes a prediction. Along the way, we'll look at the common terms you'll see in medical imaging papers and what they actually mean.</p>
<p>A companion <a href="https://github.com/lakshmi-mahabaleshwara/healthtech-playground/blob/main/what_happens_before_the_model.ipynb">notebook</a> lets you run most of these steps yourself instead of just reading about them.</p>
<h3 id="heading-what-well-cover">What We'll Cover:</h3>
<ul>
<li><p><a href="#heading-what-youll-learn">What You'll Learn</a></p>
</li>
<li><p><a href="#heading-from-image-to-dataset">From Image to Dataset</a></p>
<ul>
<li><p><a href="#heading-1-acquisition">1. Acquisition</a></p>
</li>
<li><p><a href="#heading-2-anonymization-de-identification-and-pseudonymization">2. Anonymization, de-identification, and pseudonymization</a></p>
</li>
<li><p><a href="#heading-3-safe-harbor-and-expert-determination">3. Safe Harbor and Expert Determination</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-from-dataset-to-model-input">From Dataset to Model Input</a></p>
<ul>
<li><p><a href="#heading-4-preprocessing">4. Preprocessing</a></p>
</li>
<li><p><a href="#heading-5-normalization">5. Normalization</a></p>
</li>
<li><p><a href="#heading-6-annotation-and-label">6. Annotation and label</a></p>
</li>
<li><p><a href="#heading-7-dataset-splitting">7. Dataset splitting</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-from-model-to-prediction">From Model to Prediction</a></p>
<ul>
<li><p><a href="#heading-8-classification-detection-and-segmentation">8. Classification, detection, and segmentation</a></p>
</li>
<li><p><a href="#heading-9-augmentation">9. Augmentation</a></p>
</li>
<li><p><a href="#heading-10-postprocessing">10. Postprocessing</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-from-one-hospital-to-the-real-world">From One Hospital to the Real World</a></p>
<ul>
<li><p><a href="#heading-11-harmonization">11. Harmonization</a></p>
</li>
<li><p><a href="#heading-12-retrospective-and-prospective-validation">12. Retrospective and prospective validation</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-putting-it-together">Putting it together</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-youll-learn">What You'll Learn</h2>
<ul>
<li><p>What each stage of a medical imaging pipeline is called, and what actually happens at each stage</p>
</li>
<li><p>The difference between anonymization, de-identification, and pseudonymization</p>
</li>
<li><p>Why "annotation" and "normalization" can mean different things depending on the context</p>
</li>
<li><p>How classification, detection, and segmentation differ on the same image</p>
</li>
<li><p>What harmonization fixes, and why validation design can matter more than model choice</p>
</li>
</ul>
<h2 id="heading-from-image-to-dataset">From Image to Dataset</h2>
<h3 id="heading-1-acquisition">1. Acquisition</h3>
<p>Acquisition is when the image is created. It includes the imaging machine, its settings, and how the patient is positioned.</p>
<p>Two chest X-rays of the same patient may look different if they were taken using different machines. The manufacturer, detector, exposure settings, and image-processing software can all affect the final image.</p>
<p>Patient's position matters too. For example, a standing <strong>PA (Posteroanterior)</strong> chest X-ray can look very different from a portable <strong>AP (Anteroposterior)</strong> X-ray taken while a patient is lying in bed.</p>
<p>One important difference is the apparent size of the heart. This can affect what a model learns.</p>
<p>A lot of this information is stored in the <strong>DICOM</strong> file.</p>
<p>DICOM (Digital Imaging and Communications in Medicine) is the standard format used to store and communicate medical images. A DICOM file contains much more than pixels. It can also contain information about the patient, scanner, study, image orientation, pixel spacing, and other details.</p>
<p>The image used in this tutorial originally arrived as a JPEG, so the original clinical DICOM metadata wasn't available. For demonstration, the notebook wraps the image in a new DICOM file and adds a few useful fields.</p>
<pre><code class="language-python">ds = pydicom.dcmread("synthetic_cxr.dcm")

for tag in ["Modality", "BodyPartExamined", "ViewPosition",
            "Manufacturer", "ManufacturerModelName", "KVP", "PixelSpacing"]:
    print(f"{tag:24s} {ds[tag].value}")
</code></pre>
<p>One field worth paying attention to is <strong>PixelSpacing</strong>. It tells you the physical size represented by each pixel, usually in millimeters. Two images can both be 512 × 512 pixels but cover different physical areas.</p>
<p>So if you want to measure something in millimeters, you can't simply count pixels. You also need to know the pixel spacing.</p>
<h3 id="heading-2-anonymization-de-identification-and-pseudonymization">2. Anonymization, De-identification, and Pseudonymization</h3>
<p>These three terms are often used interchangeably, but they have different meanings.</p>
<h4 id="heading-de-identification">De-identification</h4>
<p>De-identification removes or changes information that could identify a person.</p>
<p>For example:</p>
<pre><code class="language-plaintext">Patient name
Medical record number
Date of birth
Phone numbers
Other identifying information
</code></pre>
<p>The goal is to reduce the chance that the data can be linked back to a person.</p>
<h4 id="heading-pseudonymization">Pseudonymization</h4>
<p>Pseudonymization replaces an identifier with a code.</p>
<p>For example:</p>
<pre><code class="language-plaintext">Jane Doe → SUBJ_0041
</code></pre>
<p>The important difference is that a separate key can still connect <code>SUBJ_0041</code> back to Jane Doe.</p>
<p>Hospitals may need this because they sometimes need to find the original patient again.</p>
<h4 id="heading-anonymization">Anonymization</h4>
<p>Anonymization aims to make re-identification no longer reasonably possible.</p>
<p>Unlike pseudonymization, there's no retained key that can be used to reconnect the data to the person.</p>
<p>A simple question can help:</p>
<blockquote>
<p>Can this data still be linked back to the person using additional information?</p>
</blockquote>
<p>If the answer is yes because a separate key exists, you're generally dealing with pseudonymization rather than true anonymization.</p>
<p>The exact legal meaning depends on the country and regulation, so researchers should be careful when using these terms.</p>
<h4 id="heading-the-pixels-can-contain-identifiers-too">The pixels can contain identifiers, too</h4>
<p>Removing information from the DICOM header is only one part of the job.</p>
<p>Sometimes text is actually drawn into the image.</p>
<p>For example:</p>
<pre><code class="language-plaintext">Patient name
Date
Hospital name
R
PORTABLE
</code></pre>
<p>This is called <strong>burned-in annotation.</strong></p>
<p>Removing the DICOM metadata does nothing to this text. You have to find the text in the pixels and remove it.</p>
<p>This is harder than it sounds. A simple rule such as "look for bright pixels near the top of the image" will find more than just text. Bones such as the clavicle and ribs are also bright.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/f7c468f5-87bf-43ed-b035-40658803e6d2.png" alt="Chest X-ray showing identifying text and anatomical structures, illustrating why burned-in annotations cannot be removed by simply deleting DICOM metadata." style="display: block;" width="676" height="520" loading="lazy">

<p>That means a real de-identification pipeline usually combines several techniques:</p>
<pre><code class="language-plaintext">Text detection
OCR
Image-region rules
DICOM metadata removal
Validation
</code></pre>
<p>The goal isn't just to remove text. It's to prove that the text was actually removed.</p>
<p>For example:</p>
<pre><code class="language-python">IDENTIFIERS = [
    "PatientName",
    "PatientID",
    "PatientBirthDate",
    "PatientSex",
    "InstitutionName",
    "ReferringPhysicianName",
    "StudyDate",
    "StudyTime",
    "AccessionNumber"
]

for tag in IDENTIFIERS:
    if tag in ds:
        ds[tag].value = ""

# Records that an identity-removal process was performed.
# This field alone does not make the dataset de-identified.
ds.PatientIdentityRemoved = "YES"
</code></pre>
<p>The pixel-level part is where a simple demo and a real production pipeline are very different.</p>
<h3 id="heading-3-safe-harbor-and-expert-determination">3. Safe Harbor and Expert Determination</h3>
<p>If you work with US healthcare data, you'll often see two HIPAA terms: <strong>Safe Harbor</strong> and <strong>Expert Determination</strong>.</p>
<p>They are two ways to determine whether protected health information has been de-identified under HIPAA.</p>
<h4 id="heading-safe-harbor">Safe Harbor</h4>
<p>Safe Harbor is a checklist. It requires removing 18 categories of identifiers, including things such as:</p>
<pre><code class="language-plaintext">Names
Geographic information smaller than a state
Certain dates
Phone numbers
Medical record numbers
Device identifiers
Full-face photographs
</code></pre>
<p>It's relatively straightforward because you can follow a defined list.</p>
<h4 id="heading-expert-determination">Expert Determination</h4>
<p>Expert Determination takes a different approach. A qualified expert evaluates the risk of re-identification and documents why the remaining risk is very small. This can allow researchers to keep information that Safe Harbor would require them to remove.</p>
<p>For example, exact dates can be very useful when studying how a disease changes over time.</p>
<p>So there's a trade-off:</p>
<p><strong>Safe Harbor is simpler and more restrictive</strong></p>
<p><strong>Expert Determination is more flexible, but requires a documented risk assessment</strong></p>
<p>These are US HIPAA concepts. Other countries have different privacy laws and definitions.</p>
<p>For example, the GDPR in the EU and India's DPDP Act have their own approaches to anonymous and pseudonymous data.</p>
<h2 id="heading-from-dataset-to-model-input">From Dataset to Model Input</h2>
<h3 id="heading-4-preprocessing">4. Preprocessing</h3>
<p>Preprocessing is what you do to an image before giving it to a model.</p>
<p>Common preprocessing steps include:</p>
<ul>
<li><p>Resizing</p>
</li>
<li><p>Windowing</p>
</li>
<li><p>Changing orientation</p>
</li>
<li><p>Normalizing pixel values</p>
</li>
</ul>
<img src="https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/09d29749-7e91-4be6-8569-bcea52249e4a.png" alt="Chest X-ray before and after preprocessing, illustrating changes such as resizing, windowing, and orientation." style="display: block;" width="1597" height="408" loading="lazy">

<h4 id="heading-resizing">Resizing</h4>
<p>Models usually expect images of a fixed size. Medical images can come in many different sizes, so they're often resized.</p>
<p>But resizing changes the relationship between pixels and physical space.</p>
<p>For example, if you resize an image from 1024 × 1024 to 512 × 512, each pixel now represents a different physical area.</p>
<p>So if you need physical measurements, such as distances or areas in millimeters, you need to account for the original or resampled pixel spacing.</p>
<h4 id="heading-windowing">Windowing</h4>
<p>Windowing selects a range of intensity values and maps that range to the display range. Values outside the range are clipped.</p>
<p>Radiologists commonly use different window settings when looking at CT images because different tissues become easier to see under different windows.</p>
<p>Windowing changes how the image is represented or displayed. It doesn't mean the original image data has been changed.</p>
<p>The notebook demonstrates windowing using the 2nd and 98th percentiles:</p>
<pre><code class="language-python">lo, hi = np.percentile(img, [2, 98])
windowed = np.clip(
    (img.astype(float) - lo) / (hi - lo),
    0,
    1
)
</code></pre>
<h4 id="heading-orientation">Orientation</h4>
<p>Medical images also contain information about orientation. Getting this wrong means left and right are swapped, which, for a chest X-ray, is a serious mistake.</p>
<h3 id="heading-5-normalization">5. Normalization</h3>
<p>Here's the first word with dual meanings.</p>
<p>In machine learning, normalization usually means transforming numerical values into a more consistent scale or distribution so that training behaves more predictably. Two common versions:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/a5015387-4f30-482e-bf9b-54fde7d56b15.png" alt="Chest X-ray showing different pixel-value normalization methods, comparing the original image with min-max and z-score normalized versions." style="display: block;" width="1599" height="516" loading="lazy">

<ul>
<li><strong>Min-max</strong>: rescale so the lowest value becomes 0 and the highest becomes 1.</li>
</ul>
<pre><code class="language-python">f = img.astype(np.float32)

minmax = (f - f.min()) / (f.max() - f.min())
</code></pre>
<ul>
<li><p><strong>Z-score</strong>: subtract the mean and divide by the standard deviation, giving a mean of 0 and a standard deviation of 1.</p>
<pre><code class="language-python">zscore = (f - f.mean()) / f.std()
</code></pre>
</li>
</ul>
<p>Min-max normalization has one weakness. It depends directly on the minimum and maximum values. A very bright pixel, such as one caused by a device or noise, can change the range and squeeze most of the image into a smaller part of the scale.</p>
<p>Z-score normalization is less dependent on the minimum and maximum, although extreme values can still affect the mean and standard deviation.</p>
<p>In medical imaging, normalization can also be applied consistently across images or datasets, but making images from different scanners or institutions comparable is more specifically described as <strong>harmonization</strong>. The distinction becomes important when you are dealing with multiple sites, as explained in section 10.</p>
<h3 id="heading-6-annotation-and-label">6. Annotation and Label</h3>
<p>Both describe information associated with an image, but they differ in what that information represents.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/9c7da7a1-5382-4136-8b3b-07293e794010.png" alt="Chest X-ray illustrating the difference between an image-level label and a spatial annotation, with a lung region marked on the image." style="display: block;" width="1211" height="537" loading="lazy">

<p>A <strong>label</strong> usually describes the image as a whole.</p>
<p>For example:</p>
<pre><code class="language-plaintext">Image → Pneumonia
</code></pre>
<p>An <strong>annotation</strong> usually tells you where something is in the image.</p>
<p>It could be:</p>
<ul>
<li><p>A point</p>
</li>
<li><p>Bounding box</p>
</li>
<li><p>Polygon</p>
</li>
<li><p>Contour</p>
</li>
<li><p>Pixel-level mask</p>
</li>
</ul>
<p>For example:</p>
<pre><code class="language-plaintext">Image → Lung mask
</code></pre>
<p>The key difference is <strong>location</strong>.</p>
<p>A label tells you <em>what</em> is present while an annotation can tell you <em>what</em> is present and <em>where</em> it is.</p>
<p>Annotations also take much more effort to create.</p>
<p>A large dataset may have millions of image-level labels extracted from radiology reports, but only a small subset may have detailed masks created by clinicians.</p>
<p>That shortcut has a cost. A report may say "pneumonia," but that doesn't necessarily tell you exactly which pixels show pneumonia. This can lead to <strong>noisy labels</strong>.</p>
<h3 id="heading-7-dataset-splitting">7. Dataset Splitting</h3>
<p>One of the most important decisions in a medical imaging study is <strong>how you split the data</strong>.</p>
<p>The split should usually happen at the <strong>patient level</strong>, not the image level.</p>
<p>Imagine a patient has five X-rays. If you put three images into training and two into testing, the model has already seen images from that patient during training. Patient-specific characteristics can appear in both sets. This is a form of <strong>data leakage</strong>.</p>
<p>The same idea applies to:</p>
<ul>
<li><p>Multiple scans from the same patient</p>
</li>
<li><p>Multiple images from the same study</p>
</li>
<li><p>Images derived from the same original scan</p>
</li>
</ul>
<p>The goal is simple: the test set should contain patients the model didn't see during training.</p>
<p>So instead of:</p>
<p><strong>Split images and assign patients</strong></p>
<p>do this:</p>
<p><strong>Split patients and assign their images</strong></p>
<p>This is one of those details that can have a bigger effect on your results than changing the model architecture.</p>
<h2 id="heading-from-model-to-prediction">From Model to Prediction</h2>
<h3 id="heading-8-classification-detection-and-segmentation">8. Classification, Detection, and Segmentation</h3>
<p>Take the same chest X-ray and ask three different questions.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/a06f3760-0fa8-4b37-ba1a-567a77c2ae25.png" alt="Chest X-ray showing three model outputs: an image-level classification label, a bounding box for detection, and a pixel-level segmentation mask." style="display: block;" width="1593" height="556" loading="lazy">

<h4 id="heading-classification">Classification</h4>
<p>Is there pneumonia?</p>
<p>The model returns a label or probability.</p>
<pre><code class="language-plaintext">Pneumonia: 0.92
</code></pre>
<p>It tells you <strong>what</strong> is in the image. It doesn't tell you where.</p>
<h4 id="heading-detection">Detection</h4>
<p>Where is the abnormality? The model returns a bounding box.</p>
<pre><code class="language-plaintext">[x, y, width, height]
</code></pre>
<p>It tells you roughly where the finding is. It doesn't describe its exact shape.</p>
<h4 id="heading-segmentation">Segmentation</h4>
<p>Which pixels belong to the abnormality?</p>
<p>The model returns a pixel-level mask. This gives you the shape and location of the finding.</p>
<p>A simple way to remember it:</p>
<p><strong>Classification = What?</strong></p>
<p><strong>Detection = Where?</strong></p>
<p><strong>Segmentation = Which pixels?</strong></p>
<p>The amount of annotation work usually increases as you move from classification to detection to segmentation.</p>
<p>So choose the simplest task that answers your question. If you only need to know whether a scan is abnormal, you probably don't need segmentation.</p>
<h3 id="heading-9-augmentation">9. Augmentation</h3>
<p>Augmentation creates new training examples by transforming existing images.</p>
<p>Common transformations include:</p>
<ul>
<li><p>Rotation</p>
</li>
<li><p>Translation</p>
</li>
<li><p>Zoom</p>
</li>
<li><p>Brightness changes</p>
</li>
</ul>
<p>This can be useful when medical datasets are small.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/4eb5cc4a-b415-4d97-8f8a-341ce433576e.png" alt="Chest X-ray showing examples of image augmentation, including rotation and horizontal flipping, demonstrating how transformations can change anatomical orientation." style="display: block;" width="1595" height="518" loading="lazy">

<p>But medical images have an important constraint: the transformed image should still look like something that could realistically happen to a patient.</p>
<p>For example, a horizontal flip is common in natural-image machine learning.</p>
<p>Flip a photo of a cat and you still have a cat.</p>
<p>But flip a chest X-ray and the heart moves to the other side. You may have just created an image that looks like <strong>dextrocardia</strong>, where the heart is on the right side.</p>
<p>The same problem happens with markers. A right-side marker can suddenly appear on the left. The letter itself is also mirrored. A model doesn't automatically understand that this is anatomically wrong.</p>
<p>Other augmentations can cause problems, too. A large brightness change might hide important findings. An aggressive crop might remove part of the lungs.</p>
<p>So before using an augmentation, ask: could this image realistically come from a real scanner and a real patient?</p>
<p>If not, don't use it.</p>
<pre><code class="language-python"># Reasonable: small rotation
M = cv2.getRotationMatrix2D((cx, cy), 7, 1.0)
rotated = cv2.warpAffine(
    img,
    M,
    (w, h),
    borderMode=cv2.BORDER_REPLICATE
)

# Potentially problematic for a chest X-ray:
flipped = img[:, ::-1]
</code></pre>
<h3 id="heading-10-postprocessing">10. Postprocessing</h3>
<p>Postprocessing happens after the model produces its output.</p>
<p>A segmentation model usually produces a probability for every pixel. After applying a threshold, the mask may contain small unwanted regions or holes.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/262a38f4-9da2-416e-98ba-7828b19df58f.png" alt="Lung segmentation mask before and after postprocessing, showing removal of small connected regions and filling of holes." style="display: block;" width="1591" height="473" loading="lazy">

<p>For example, the notebook's raw mask contains:</p>
<ul>
<li><p>Two large lung regions</p>
</li>
<li><p>Fourteen small unwanted regions</p>
</li>
</ul>
<p>A common cleanup step is to keep only the largest connected components.</p>
<p>Since we expect two lungs, we can keep the two largest regions. We can also fill small holes.</p>
<pre><code class="language-python">labelled, n = ndimage.label(mask &gt; 0)

sizes = ndimage.sum(
    mask &gt; 0,
    labelled,
    range(1, n + 1)
)

keep = np.isin(
    labelled,
    np.argsort(sizes)[-2:] + 1
)

clean = ndimage.binary_fill_holes(keep)
</code></pre>
<p>In this example, the cleanup reduces the number of connected components from 16 to 2 while changing less than 5% of the pixels.</p>
<p>That illustrates an <strong>important point about evaluation:</strong> pixel-overlap metric might barely change, even though the structure of the prediction has changed significantly.</p>
<p>For some applications, the number and shape of connected regions matter more than a small change in pixel overlap. So choose metrics that match the errors you actually care about.</p>
<p>Also, be careful with postprocessing. If your model needs a lot of cleanup before the result looks good, the cleanup may be hiding problems in the model.</p>
<p>Always look at the raw output, too.</p>
<h2 id="heading-from-one-hospital-to-the-real-world">From One Hospital to the Real World</h2>
<h3 id="heading-11-harmonization">11. Harmonization</h3>
<p>Imagine two hospitals use different scanners.</p>
<p>The images may have different:</p>
<ul>
<li><p>Brightness</p>
</li>
<li><p>Contrast</p>
</li>
<li><p>Noise</p>
</li>
<li><p>Resolution</p>
</li>
<li><p>Image-processing characteristics</p>
</li>
</ul>
<p>A model trained mostly on Hospital A may learn some of these differences instead of learning features related to the disease. It may perform well on Hospital A but fail on Hospital B.</p>
<p>This is where <strong>harmonization</strong> can help.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69fd77e89f93a850a46d376f/309ae26c-a241-4af2-a59f-3004cad885d5.png" alt="Chest X-rays from different imaging sources before and after harmonization, illustrating differences in brightness and contrast between sites." style="display: block;" width="1503" height="1017" loading="lazy">

<p>Harmonization tries to make data from different sources more comparable while preserving the information that matters.</p>
<p>One simple example is <strong>histogram matching</strong>.</p>
<p>It adjusts the pixel-value distribution of one image to look more like a reference image.</p>
<pre><code class="language-python">from skimage.exposure import match_histograms

harmonized = match_histograms(
    image_from_hospital_b,
    reference_from_hospital_a
)
</code></pre>
<p>This can help with brightness and contrast differences, but it can't solve everything.</p>
<p>If two scanners have different resolution, noise characteristics, or image-processing pipelines, histogram matching alone isn't enough.</p>
<p>And if information was lost during image acquisition or clipping, harmonization can't magically recover it.</p>
<p>A useful rule of thumb is:</p>
<p><strong>Normalization makes numerical values more consistent.</strong></p>
<p><strong>Harmonization addresses systematic differences between data sources.</strong></p>
<h3 id="heading-12-retrospective-and-prospective-validation">12. Retrospective and Prospective Validation</h3>
<p>This section is about study design.</p>
<h4 id="heading-retrospective">Retrospective</h4>
<p>A retrospective study uses data that already exists.</p>
<p>For example:</p>
<blockquote>
<p>We collected 4,000 chest X-rays from the hospital archive and tested our model on them.</p>
</blockquote>
<p>This is common in medical AI because it's relatively fast and inexpensive.</p>
<p>But it also creates opportunities for bias. Researchers may make decisions about which patients to include, which scans to exclude, which hospital to use, and which threshold to choose.</p>
<p>These decisions can unintentionally make the results look better.</p>
<h4 id="heading-prospective">Prospective</h4>
<p>In a prospective study, you define the study plan first and then collect data going forward.</p>
<p>For example:</p>
<blockquote>
<p>We define the patient population, evaluation criteria, and success metrics before collecting the images.</p>
</blockquote>
<p>This can reduce some sources of bias because important decisions are made before seeing the results.</p>
<h4 id="heading-external-validation">External validation</h4>
<p>You'll also see the term <strong>external validation</strong>.</p>
<p>This means testing the model on data that is independent of the data used to develop it.</p>
<p>For example:</p>
<pre><code class="language-plaintext">Hospital A → Training
Hospital A → Internal test
Hospital B → External validation
</code></pre>
<p>An even stronger test might be:</p>
<pre><code class="language-plaintext">Hospital A + B → Development
Hospital C → External validation
</code></pre>
<p>A model that performs well on its own hospital's data but poorly at another hospital may have learned site-specific patterns instead of general disease features.</p>
<p>External validation is therefore often much more informative than simply creating another random split from the same dataset.</p>
<h2 id="heading-putting-it-together">Putting it Together</h2>
<p>Now consider this sentence from a hypothetical medical imaging paper:</p>
<blockquote>
<p>We retrospectively collected 4,120 de-identified frontal chest radiographs from two institutions. Images were resampled to 512 × 512, intensity-normalized, and harmonized across sites by histogram matching. Lung fields were manually annotated by two radiologists; segmentation output was postprocessed by largest-component selection. The model was externally validated on 890 studies from a third site.</p>
</blockquote>
<p>That paragraph contains a lot of information, but now you can decode it:</p>
<ol>
<li><p><strong>Retrospectively collected:</strong> The researchers used images that already existed.</p>
</li>
<li><p><strong>De-identified:</strong> Identifying information was removed or changed.</p>
</li>
<li><p><strong>Two institutions:</strong> The dataset comes from more than one source.</p>
</li>
<li><p><strong>Resampled to 512 × 512:</strong> Images were converted to a common image size.</p>
</li>
<li><p><strong>Intensity-normalized:</strong> Pixel values were transformed to a more consistent numerical scale.</p>
</li>
<li><p><strong>Harmonized:</strong> The researchers tried to reduce systematic differences between the two sites.</p>
</li>
<li><p><strong>Manually annotated by two radiologists:</strong> Experts created spatial information showing where the lung fields are.</p>
</li>
<li><p><strong>Postprocessed:</strong> The model's raw segmentation output was cleaned up.</p>
</li>
<li><p><strong>Externally validated:</strong> The model was tested on independent data from another site.</p>
</li>
</ol>
<p>And that last part is especially important. A model that works well only on the data it was developed on tells you much less about how it will perform in the real world.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>A medical imaging paper can describe an entire data pipeline in just a few sentences.</p>
<p>Once you understand the terminology, you can start reading those sentences differently.</p>
<p>Instead of just looking at the model and its accuracy, you can ask:</p>
<ul>
<li><p>Where did the images come from?</p>
</li>
<li><p>What scanner was used?</p>
</li>
<li><p>Were patients kept separate between training and testing?</p>
</li>
<li><p>How were identifiers removed?</p>
</li>
<li><p>Could there be burned-in text?</p>
</li>
<li><p>Who created the labels or annotations?</p>
</li>
<li><p>What preprocessing was performed?</p>
</li>
<li><p>How were pixel values normalized?</p>
</li>
<li><p>Were different hospitals or scanners harmonized?</p>
</li>
<li><p>Was the model externally validated?</p>
</li>
<li><p>Was the test data truly independent?</p>
</li>
<li><p>What happened to the model's output after prediction?</p>
</li>
</ul>
<p>The model is only one part of a medical imaging pipeline. Very often, the more important questions come <strong>before the model ever sees an image</strong>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Preprocess Medical Images for Machine Learning – A Guide Using Chest X-Rays ]]>
                </title>
                <description>
                    <![CDATA[ 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 ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-preprocess-medical-images-for-machine-learning/</link>
                <guid isPermaLink="false">6a21b25709761aac249473c9</guid>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ healthcare ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Medical Imaging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ data-engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Data Preprocessing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lakshmi Mahabaleshwara ]]>
                </dc:creator>
                <pubDate>Thu, 04 Jun 2026 17:13:59 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/eab58d7c-f63a-41ae-a01e-52a65b0be17c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>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.</p>
<p>In 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.</p>
<p>We’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.</p>
<h2 id="heading-what-youll-learn-in-this-article">What You'll Learn in This Article</h2>
<p>By the end of this article, you'll know how to:</p>
<ul>
<li><p>Approach healthcare data preprocessing differently from preprocessing structured data, and recognize where standard techniques fall short</p>
</li>
<li><p>Validate a medical imaging dataset before training to catch corrupted files, mislabels, and data leakage between train and test</p>
</li>
<li><p>Apply six core preprocessing techniques for medical images</p>
</li>
<li><p>Build a complete preprocessing pipeline for chest X-rays using Python with OpenCV.</p>
</li>
</ul>
<h2 id="heading-what-well-cover"><strong>What We'll Cover:</strong></h2>
<ul>
<li><p><a href="#heading-why-preprocessing-data-matters-more-in-healthcare">Why Preprocessing Data Matters More in Healthcare</a></p>
</li>
<li><p><a href="#heading-the-dataset">The Dataset</a></p>
</li>
<li><p><a href="#heading-before-preprocessing-validate-the-dataset">Before Preprocessing: Validate the Dataset</a></p>
</li>
<li><p><a href="#heading-the-six-pillars-of-healthcare-imaging-preprocessing">The Six Pillars of Healthcare Imaging Preprocessing</a></p>
</li>
<li><p><a href="#heading-pillar-1-scaling-making-the-numbers-play-fair">Pillar 1: Scaling — Making the Numbers Play Fair</a></p>
</li>
<li><p><a href="#heading-pillar-2-normalization-centering-the-data">Pillar 2: Normalization — Centering the Data</a></p>
</li>
<li><p><a href="#heading-pillar-3-guiding-the-models-attention">Pillar 3: Guiding the Model's Attention</a></p>
</li>
<li><p><a href="#heading-pillar-4-handling-missing-data">Pillar 4: Handling Missing Data</a></p>
</li>
<li><p><a href="#heading-pillar-5-resizing-amp-resampling-fitting-everything-in-the-same-frame">Pillar 5: Resizing &amp; Resampling — Fitting Everything in the Same Frame</a></p>
</li>
<li><p><a href="#heading-pillar-6-denoising-amp-artifact-handling-cleaning-the-window">Pillar 6: Denoising &amp; Artifact Handling — Cleaning the Window</a></p>
</li>
<li><p><a href="#heading-putting-it-all-together-a-complete-pipeline">Putting it All together: A Complete Pipeline</a></p>
</li>
<li><p><a href="#heading-try-it-yourself">Try it Yourself</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-why-preprocessing-data-matters-more-in-healthcare">Why Preprocessing Data Matters More in Healthcare</h2>
<p>Imagine 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.</p>
<p>The 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.</p>
<img 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." style="display: block;" width="1168" height="558" loading="lazy">

<p>Healthcare data tends to be messier than what most ML practitioners are used to:</p>
<ul>
<li><p>Images come from different machines, hospitals, and acquisition protocols</p>
</li>
<li><p>Labels are inconsistent, sometimes missing, sometimes wrong</p>
</li>
<li><p>Patient data is incomplete</p>
</li>
<li><p>Image sizes, contrast levels, and orientations vary across sources</p>
</li>
</ul>
<p>Poor preprocessing often leads to models that perform well on benchmark datasets but struggle to generalize to data collected from different hospitals or imaging devices.</p>
<h2 id="heading-the-dataset">The Dataset</h2>
<p>This guide uses the <strong>Chest X-Ray Pneumonia dataset</strong> by Paul Mooney on Kaggle. It's a strong choice for learning preprocessing because:</p>
<ul>
<li><p>It contains around 5,800 pediatric chest X-rays</p>
</li>
<li><p>It has two clear classes — Normal and Pneumonia</p>
</li>
<li><p>It's already organized into train, validation, and test folders</p>
</li>
<li><p>The images are recognizable without specialized medical training</p>
</li>
<li><p>It exhibits almost every preprocessing challenge worth learning</p>
</li>
</ul>
<p>The dataset is available at <a href="https://www.kaggle.com/datasets/paultimothymooney/chest-xray-pneumonia">Kaggle: Chest X-Ray Pneumonia</a>.</p>
<h3 id="heading-folder-structure">Folder Structure</h3>
<p>After downloading, the dataset is organized like this:</p>
<pre><code class="language-plaintext">chest_xray/
├── train/
│   ├── NORMAL/
│   └── PNEUMONIA/
├── val/
│   ├── NORMAL/
│   └── PNEUMONIA/
└── test/
    ├── NORMAL/
    └── PNEUMONIA/
</code></pre>
<p>Side-by-side comparison — Normal vs Pneumonia chest X-ray:</p>
<img 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." style="display: block;" width="592" height="195" loading="lazy">

<p>A quick first look at one of the images:</p>
<pre><code class="language-python">import os
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import cv2

DATA_DIR = "chest_xray"
TRAIN_DIR = os.path.join(DATA_DIR, "train")

# Peek at a sample image
sample_path = os.path.join(TRAIN_DIR, "NORMAL", os.listdir(os.path.join(TRAIN_DIR, "NORMAL"))[0])
sample_image = cv2.imread(sample_path, cv2.IMREAD_GRAYSCALE)

print(f"Image shape: {sample_image.shape}")
print(f"Pixel range: {sample_image.min()} to {sample_image.max()}")
print(f"Data type: {sample_image.dtype}")
</code></pre>
<p>The 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.</p>
<h2 id="heading-before-preprocessing-validate-the-dataset">Before Preprocessing: Validate the Dataset</h2>
<p>Before 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.</p>
<p>A simple validation function:</p>
<pre><code class="language-python">def validate_dataset(data_dir):
    """Scan a dataset folder and flag common data quality issues."""
    corrupted = []
    too_small = []
    nearly_black = []
    total = 0
    
    for class_name in os.listdir(data_dir):
        class_path = os.path.join(data_dir, class_name)
        if not os.path.isdir(class_path):
            continue
        for fname in os.listdir(class_path):
            fpath = os.path.join(class_path, fname)
            total += 1
            try:
                img = cv2.imread(fpath, cv2.IMREAD_GRAYSCALE)
                if img is None:
                    corrupted.append(fpath)
                    continue
                if img.shape[0] &lt; 100 or img.shape[1] &lt; 100:
                    too_small.append(fpath)
                if img.mean() &lt; 5:
                    nearly_black.append(fpath)
            except Exception:
                corrupted.append(fpath)
    
    print(f"Total files scanned: {total}")
    print(f"Corrupted: {len(corrupted)}")
    print(f"Too small: {len(too_small)}")
    print(f"Nearly black: {len(nearly_black)}")
    return corrupted, too_small, nearly_black

validate_dataset(TRAIN_DIR)
</code></pre>
<p>Common issues this catches:</p>
<ul>
<li><p><strong>Corrupted files</strong> — files that won't open at all</p>
</li>
<li><p><strong>Empty or nearly-black images</strong> — failed acquisitions or saved-as-blank files</p>
</li>
<li><p><strong>Wrong dimensions</strong> — thumbnails or partial downloads mixed in</p>
</li>
<li><p><strong>Duplicate images</strong> — the same scan appearing in both train and test (this causes data leakage)</p>
</li>
<li><p><strong>Mislabeled images</strong> — a normal X-ray placed in the pneumonia folder</p>
</li>
</ul>
<p><strong>⚠️ This step is critical</strong>, 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.</p>
<h2 id="heading-the-six-pillars-of-healthcare-imaging-preprocessing"><strong>The Six Pillars of Healthcare Imaging Preprocessing</strong></h2>
<p>Preprocessing 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.</p>
<h2 id="heading-pillar-1-scaling-making-the-numbers-play-fair">Pillar 1: Scaling — Making the Numbers Play Fair</h2>
<p>Imagine 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 <em>scales</em> are completely different. Comparing them meaningfully means putting both collections on the same measuring system.</p>
<p>In 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.</p>
<img 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." style="display: block;" width="1168" height="558" loading="lazy">

<p><strong>The fix:</strong> Divide every pixel by its maximum possible value, bringing everything into the 0-to-1 range.</p>
<pre><code class="language-python">image = cv2.imread(sample_path, cv2.IMREAD_GRAYSCALE)

# Scale to [0, 1]
image_scaled = image.astype(np.float32) / 255.0

print(f"Before scaling: {image.min()} to {image.max()}")
print(f"After scaling:  {image_scaled.min():.3f} to {image_scaled.max():.3f}")
</code></pre>
<p><strong>Takeaway:</strong> Pixel scaling follows the same principle as scaling any numerical feature. The values simply happen to be arranged as an image rather than a column.</p>
<h2 id="heading-pillar-2-normalization-centering-the-data">Pillar 2: Normalization — Centering the Data</h2>
<p>Imagine 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.</p>
<p>In 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.</p>
<p><strong>The fix:</strong> Subtract the mean, divide by the standard deviation.</p>
<pre><code class="language-python"># Compute mean and std from the TRAINING set only — never from validation or test
def compute_train_stats(train_dir, sample_limit=1000):
    """Compute pixel mean and std across the training set."""
    pixel_values = []
    count = 0
    for class_name in os.listdir(train_dir):
        class_path = os.path.join(train_dir, class_name)
        for fname in os.listdir(class_path):
            if count &gt;= sample_limit:
                break
            img = cv2.imread(os.path.join(class_path, fname), cv2.IMREAD_GRAYSCALE)
            if img is not None:
                pixel_values.append(img.astype(np.float32).flatten() / 255.0)
                count += 1
    pixels = np.concatenate(pixel_values)
    return pixels.mean(), pixels.std()

train_mean, train_std = compute_train_stats(TRAIN_DIR)
image_normalized = (image_scaled - train_mean) / train_std
</code></pre>
<p><strong>⚠️</strong> Avoid 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.</p>
<p><strong>Takeaway:</strong> Centering 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.</p>
<h2 id="heading-pillar-3-guiding-the-models-attention">Pillar 3: Guiding the Model's Attention</h2>
<p>Imagine a child walking into a crowded pet store. Instead of describing every animal in sight, a parent points to the features that matter: <em>“Look at the soft fur, the fluffy tail, and the nice small size.”</em> The child learns where to focus their attention.</p>
<p>Medical image preprocessing does something similar. It highlights the regions and features most relevant to the diagnostic task.</p>
<ul>
<li><p><strong>Region-of-interest (ROI) cropping</strong> — focus on the lung field and discard the patient's arms, machine borders, and any imprinted text</p>
</li>
<li><p><strong>Contrast enhancement</strong> — use techniques like CLAHE (Contrast Limited Adaptive Histogram Equalization) to make subtle lung textures more visible</p>
</li>
<li><p><strong>Channel selection</strong> — for images stored as RGB but containing grayscale information, convert to single-channel input to reduce noise</p>
</li>
</ul>
<img 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." style="display: block;" width="1168" height="588" loading="lazy">

<p>CLAHE applied to an X-ray:</p>
<pre><code class="language-python"># CLAHE enhances local contrast — useful for X-rays
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
image_enhanced = clahe.apply(image)

# Visualize the difference
fig, axes = plt.subplots(1, 2, figsize=(12, 6))
axes[0].imshow(image, cmap='gray')
axes[0].set_title('Original')
axes[1].imshow(image_enhanced, cmap='gray')
axes[1].set_title('After CLAHE')
plt.show()
</code></pre>
<p><strong>Takeaway:</strong> The 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.</p>
<h2 id="heading-pillar-4-handling-missing-data">Pillar 4: Handling Missing Data</h2>
<p>Imagine 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.</p>
<p>In medical imaging, missing data can mean corrupted files, missing labels, or incomplete studies rather than empty spreadsheet cells.</p>
<p>The same three strategies — drop, impute, flag — still apply, just with different mechanics:</p>
<pre><code class="language-python"># Strategy 1: Drop — remove unreadable or empty images
def is_valid_image(path):
    try:
        img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
        if img is None:
            return False
        if img.mean() &lt; 5:           # nearly black
            return False
        if img.shape[0] &lt; 50 or img.shape[1] &lt; 50:  # too small
            return False
        return True
    except Exception:
        return False

# Strategy 2: Impute — rare for images, but possible (e.g., in painting to fill in missing patches). Generally avoided for diagnostic data.

# Strategy 3: Flag — track which patients are missing which modalities,
#   and let the model condition on availability. Common in multi-modal healthcare ML.
</code></pre>
<p><strong>Takeaway:</strong> "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.</p>
<h2 id="heading-pillar-5-resizing-amp-resampling-fitting-everything-in-the-same-frame">Pillar 5: Resizing &amp; Resampling — Fitting Everything in the Same Frame</h2>
<p>Imagine 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.</p>
<p>Medical images must often be resized to a common input size, but anatomical structures should retain their original shape.</p>
<img 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." style="display: block;" width="1168" height="674" loading="lazy">

<p><strong>The fix:</strong> Resize all images to a common shape. For medical data, <em>how</em> the resizing is done matters.</p>
<pre><code class="language-python">TARGET_SIZE = (224, 224)

# Simple resize (may distort aspect ratio)
image_resized = cv2.resize(image, TARGET_SIZE)

# Better: preserve aspect ratio with padding
def resize_with_padding(image, target_size):
    h, w = image.shape[:2]
    target_h, target_w = target_size
    scale = min(target_h / h, target_w / w)
    new_h, new_w = int(h * scale), int(w * scale)
    resized = cv2.resize(image, (new_w, new_h))
    
    pad_h = target_h - new_h
    pad_w = target_w - new_w
    top, bottom = pad_h // 2, pad_h - pad_h // 2
    left, right = pad_w // 2, pad_w - pad_w // 2
    padded = cv2.copyMakeBorder(resized, top, bottom, left, right,
                                 cv2.BORDER_CONSTANT, value=0)
    return padded

image_clean_resize = resize_with_padding(image, TARGET_SIZE)
</code></pre>
<p><strong>⚠️ Why aspect ratio matters in healthcare:</strong> Squishing 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.</p>
<p><strong>Takeaway:</strong> Models need a consistent input size, but the geometry of the anatomy needs to be preserved. Resize, but resize carefully.</p>
<h2 id="heading-pillar-6-denoising-amp-artifact-handling-cleaning-the-window">Pillar 6: Denoising &amp; Artifact Handling — Cleaning the Window</h2>
<p>Imagine 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.</p>
<p>Similarly, medical images often contain noise and acquisition artifacts that should be reduced carefully without removing clinically important details.</p>
<p>For 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.</p>
<pre><code class="language-python"># Gentle denoising — careful not to blur away clinical detail
image_denoised = cv2.medianBlur(image, ksize=3)

# Bilateral filter preserves edges better than a median filter
image_bilateral = cv2.bilateralFilter(image, d=5, sigmaColor=50, sigmaSpace=50)
</code></pre>
<p><strong>⚠️ A note of caution:</strong> Aggressive 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.</p>
<p><strong>Takeaway:</strong> Imaging 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.</p>
<h2 id="heading-putting-it-all-together-a-complete-pipeline">Putting it All Together: A Complete Pipeline</h2>
<img 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." style="display: block;" width="828" height="255" loading="lazy">

<p>Here's how the six pillars combine into a single preprocessing function for chest X-ray images:</p>
<pre><code class="language-python">def preprocess_xray(image_path, target_size=(224, 224),
                    train_mean=0.482, train_std=0.236):
    """
    Full preprocessing pipeline for chest X-ray images.
    Applies all six pillars in order.
    """
    # Pillar 4: Validate first — skip corrupted files
    if not is_valid_image(image_path):
        return None
    
    image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
    
    # Pillar 5: Resize with aspect ratio preserved
    image = resize_with_padding(image, target_size)
    
    # Pillar 6: Gentle denoising
    image = cv2.medianBlur(image, 3)
    
    # Pillar 3: Enhance contrast to highlight lung texture
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
    image = clahe.apply(image)
    
    # Pillar 1: Scale to [0, 1]
    image = image.astype(np.float32) / 255.0
    
    # Pillar 2: Normalize using training set statistics
    image = (image - train_mean) / train_std
    
    return image
</code></pre>
<h2 id="heading-try-it-yourself">Try it Yourself</h2>
<p>Every code snippet in this article is bundled into a runnable Kaggle notebook: <a href="https://www.kaggle.com/code/lakshmimahabaleshwar/chest-xray-preprocessing-kaggle">Chest X-Ray Preprocessing — Kaggle Notebook</a>. Fork it, attach the dataset, and run all the cells to see each preprocessing pillar in action on real chest X-rays.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Here's a summary of what we've discussed in this article:</p>
<table>
<thead>
<tr>
<th><strong>Pillar</strong></th>
<th><strong>Purpose</strong></th>
<th><strong>Example</strong></th>
</tr>
</thead>
<tbody><tr>
<td>Scaling</td>
<td>Standardize pixel ranges</td>
<td>0-255 → 0-1</td>
</tr>
<tr>
<td>Normalization</td>
<td>Center brightness distributions</td>
<td>z-score normalization</td>
</tr>
<tr>
<td>Attention Guidance</td>
<td>Highlight diagnostic regions</td>
<td>CLAHE</td>
</tr>
<tr>
<td>Missing Data Handling</td>
<td>Remove unusable scans</td>
<td>Corrupted files</td>
</tr>
<tr>
<td>Resizing</td>
<td>Consistent input size</td>
<td>224×224</td>
</tr>
<tr>
<td>Denoising</td>
<td>Reduce acquisition noise</td>
<td>Median filter</td>
</tr>
</tbody></table>
<p>Preprocessing for structured data is about making numbers play fair so a model can see them clearly.</p>
<p>Preprocessing 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.</p>
<p>Stepping 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.</p>
<p>If this was useful, you can find a related conceptual primer on preprocessing more broadly here: <a href="https://lakshmimahabaleshwara.substack.com/p/data-preprocessing-for-machine-learning">Data Preprocessing for Machine Learning</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
