08/06/2021

What is a lung nodule?

A lung nodule is a round area that is more dense than normal lung tissue. It shows up as a white spot on an X-ray or a CT scan. Lung nodules are usually caused by scar tissue, a healed infection that may never have made you sick. Sometimes, a nodule can be an early lung cancer.

The lung nodule usually represents a benign tumor such as a granuloma or hamartoma, but in around 20% of cases it represents a malignant cancer, especially in older adults and smokers. About 10 to 20% of patients with lung cancer are diagnosed in this way.

Causes for having a lung nodule

The causes for a lung nodule can vary from an infection to a lung cancer, including rare forms of cancer such as pulmonary lymphoma, carcinoid tumor and a solitary metastasis to the lung.

The most common benign nodule is a granuloma (inflammatory nodule), which can be due to tuberculosis or a fungal infection. Other infectious causes include a lung abscess, pneumonia or rarely a worm infection. Lung nodules can also occur in immune disorders, such as rheumatoid arthritis.

If the patient has a history of smoking or the nodule is growing, the possibility of cancer may need to be excluded through further studies and interventions, including surgical removal.

Source: Wikipedia

Estimating the area of a circular lung nodule

A circular lung nodule needs to be removed and the surgeon needs to know how much tissue to remove. The goal of this project is to estimate the area of the tumor from the X-ray image of the tumor shown earlier.

We have the X-ray image as an png image file, named nodule.png. Thus, we can use the png R library to read the png image with readPNG() and plot it on a coordinate system.

However, before we can do that we need to convert the image dimensions from pixels into centimeters [cm]. Usually, on a regular X-ray image, one pixel corresponds to 175 \(\mu m\) (microns) and since 1 \(\mu m = 10^{-4} cm\), we can approximate the image dimensions in cm using the scaling:

\[\text{#pixels} \times 175 \times 10^{-4}[cm]\]

Image dimensions

We can find the dimensions of the X-ray image, first in pixels and then in cm, using the code below, where nrow(image) gives the vertical dimension in pixels and the ncol(image) gives the horizontal dimension in pixels:

# load the png library, which must be installed first
library(png)
# load the png image from the working directory
image <- readPNG("nodule.png")
# image dimensions converted to cm, assuming 1 pixel measures 175 microns
ydim <- nrow(image)*175*1e-4 # in cm, assuming 1 pixel measures 175 microns
xdim <- ncol(image)*175*1e-4 # in cm, assuming 1 pixel measures 175 microns
c(xdim, ydim) # image dimensions in cm
## [1] 36.5225 40.1275

The ydim and xdim are the vertical and horizontal image dimensions in [cm].

X-ray image in a coordinate system

We can plot the X-ray image on a coordinate system where the dimensions are measured in centimeters. First, we create an empty plot with the same dimensions measured in centimeters as the X-ray image, using the plotting command:

# create an empty plot with the dimensions of the image
plot(NULL, type="n", ylim=c(0,ydim), xlim=c(0,xdim), xlab="x", ylab="y")

We can plot the X-ray image inside the empty plot using the rasterImage() function, where the coordinates (xleft, ybottom) of the left-most bottom corner of the image, and the coordinates (xright, ytop) of the right-most top corner are extracted from the empty plot, using par("usr").

X-ray image in a coordinate system

The call par("usr") returns a vector of the form c(x1, x2, y1, y2) with the coordinates of the plot corners.

xleft=par("usr")[1], ybottom=par("usr")[3], xright=par("usr")[2], ytop=par("usr")[4]

Below is the R code that plots the X-ray image inside the empty plot with the image dimensions specified in centimeters.

plot(NULL, type="n", ylim=c(0,ydim), xlim=c(0,xdim), xlab="x", ylab="y")
rasterImage(image, xleft=par("usr")[1], ybottom=par("usr")[3], 
            xright=par("usr")[2], ytop=par("usr")[4])

Sampling the tumor boundary

We manually sample 18 points from the tumor boundary using the mouse inside the RStudio Viewer. The manual sampling can be implemented using the locator() function, which is supported only on screen devices such as X11, windows and quartz.

Once we sample the 18 points from the boundary of the tumor, we can plot them on the top of the X-ray image and write the \(x\)-and-\(y\) coordinates of the sample points inside a csv file.

Note: All R code given in the slides up to this point would have to be run from a plain R script and the working directory must be the folder that contains the R scrip and the png image.

R code for sampling the tumor boundary

# sample points inside the RStudio Viewer
coords <- locator(n=18, type="o") 

# plot the sampled points from the list coords
plot(coords,type="o",col="purple",lwd=3)

# write the x and y coordinates of the sampled points in a csv file
write.csv(coords,file="xy.csv", row.names=FALSE)

The locator() function returns a list with the \(x\) and \(y\) coordinates of the sampled points, which are plotted on the top of the X-ray image, specified by the argument type="o". We can also plot the sampled points only with plot(coords), and we then write the coordinates into the xy.csv file.

xycoords <- read.csv("xy.csv")
x<-xycoords[,1]
y<-xycoords[,2]
plot(c(x,head(x,1)),c(y,head(y,1)),type="o",pch=20,cex=1.4, col="purple", lwd=2, 
     xlab="", ylab="", cex.axis=0.6, asp=1)

The surveyor’s method for estimating area

The surveyor estimates first the location of the center of the area. From this point we measure the distances to each polygon vertex. Then the distances are averaged to obtain \(\bar{R}\). One can think of this as an average radius of a circle approximating the area. The area \(A\) is calculated as:

\[A=\pi \bar{R}^2\]

We can find the center of the polygon by computing its center of mass \(C=(\bar{x},\bar{y})\), where \(\bar{x}\) is the average of the \(x\)-coordinates of the polygon vertices, and \(\bar{y}\) is the average of the \(y\)-coordinates of the polygon vertices:

\[\bar{x} = \frac{1}{n}\sum_{k=1}^n x_k, \quad \bar{y} = \frac{1}{n}\sum_{k=1}^n y_k\] where \(n=18\) is the number of data points sampled from the tumor boundary.

The surveyor’s method for estimating area

xbar <- mean(x)
ybar <- mean(y)
Rbar <- xycoords %>% 
  mutate(r = sqrt((x-xbar)^2 + (y - ybar)^2)) %>% 
  summarize(mean(r)) %>% pull()

Thus, the area of the approximating circle is:

pi*Rbar^2
## [1] 8.929663

We can plot the circle centered at \((\bar{x},\bar{y})\) with radius \(\bar{R}\) with:

\[x = \bar{x} + \bar{R}\cos(\alpha), \, y = \bar{y} + \bar{R}\sin(\alpha), \, \alpha \in (0,2\pi)\]

The surveyor’s method for estimating area

Computing the area of the polygon

The area of the polygon formed by the points sampled from the tumor boundary, using complex numbers, is given by:

\[A=\frac{1}{2} \text{Im}\left (\sum_{k=0}^{n-1} z_k \bar{z}_{k+1} \right )\]

z <- x + 1i*y
n<-length(x)
z_shift <- c(z[2:n], z[1])
1/2*Im(sum(z*Conj(z_shift)))
## [1] 8.780751

Circle fitting

To be added …

Fitting a circle to the sampled points