Sampling an image to get point pattern - spatstat

Here is an example where the authors have taken this image:
and analyzed using spatstat. To do that, they extracted the coordinates as it could be seen here. I would like to do the same and I am wondering how one could get a sample point pattern out of an image like this. Directly converting the image into a ppp object creates lots of artifacts.
The manual in their R package BioC2015Oles doesn't seem to exist. Thanks.

The spatstat package assumes you have already processed your image and ended up with a list of coordinates and possibly some attributes (marks) associate with each pair (x,y). Ideally you should also have a window of observation indicating where the points potentially can occur. There is a raster/image formats in spatstat called im which can store an image/raster, but it is many used to store auxiliary information from the experiment, which can be used to explain the occurrence or absence of points in areas of the observation window and not to do image processing per se.
To convert a noisy microscope image to e.g. a list of cell centres people usually use various image processing tools and techniques (watershed, morphological opening and closing, etc.). The presentation you refer to seems to build on the R package EBimage (which is on BioConductor and does have a manual), and you can try to extract the cells using that. Alternatively there are other packages in R or entirely different open source systems focusing on image analysis such as QuPath, ImageJ and many others. I cannot really guide you as to which one is the better tool for your task.

You can do
library(raster)
x <- brick('tLh2E.jpg')
#plotRGB(x)
All cells with coordinates:
xyz = rasterToPoints(x)
head(xyz)
# x y tLh2E.1 tLh2E.2 tLh2E.3
#[1,] 0.5 1103.5 222 222 224
#[2,] 1.5 1103.5 214 214 216
#[3,] 2.5 1103.5 223 223 225
#[4,] 3.5 1103.5 220 220 222
#[5,] 4.5 1103.5 197 197 199
#[6,] 5.5 1103.5 198 198 200
Or a sample:
s1 <- sampleRandom(x, 100, xy=TRUE)
s2 <- sampleRegular(x, 100, xy=TRUE)
To look at the locations of the samples
plotRGB(x)
points(s1[, 1:2])
points(s2[, 1:2], col='red', pch=20)
To create an image from a regular sample
r <- sampleRegular(x, 1000, asRaster=TRUE)
plotRGB(r)
For stratified sampling you would have to define the regions. You could draw them with raster::drawPoly() followed by rasterize, or model them see raster::predict. Here a very simple, and perhaps not very good, approach based on eye-balling. It turns out that the second, "green", layer (from the red-green-blue image) has most of the information. This gets you close:
r <- reclassify(x[[2]], rbind(c(0,100,1), c(100,175,2), c(175,255,3)))
plot(r, col=c('red', 'blue','gray'))
You can now do the following to find out which color each point has:
extract(r, s1[,1:2])

Related

Create smooth links in JointJS that route around obstacles

I'm attempting to create links in JointJS that are smooth curves but also route around obstacles. The default implementation allows us to choose routers and connectors. Routers let you define the logic to avoid obstacles and basically return a set of points to hit on the connector. The connector takes that set of points and converts it into an svg path.
Using no router or the "orthogonal" router with the "smooth" connector produces a result like this
The manhattan and metro routers have logic to avoid obstacles. You can use them with the default connector or a rounded one (default but with rounded corners) to get something like this:
For comparison, the metro router without the smooth connector looks like
Combining the smooth connector with one of those routers works great, except that you tend to get strange artifacts at the ends (and unnecessarily complex curves):
I believe this issue is that the smooth connector is just calculating bezier curves that go through the points from the router.
I thought that perhaps I could solve the problem by calculating the curves (using g.Curve.throughPoint() which created this last picture and then adjusting the very first and very last control points to be inline with the start and end points horizontally and vertically, respectively (in this case). That made far less difference than I hoped (in particular at the start):
This is the svg path of that last one:
M 150 110 C 155.78724378793083 110 161.57448757586167 98.34306652925683 170 110 C 178.42551242413833 121.65693347074317 189.48929348448416 150.79926714760109 220 160 C 250.51070651551584 169.20073285239891 300.46833848620173 158.45986488033893 320 160 C 339.53166151379827 161.54013511966107 328.6373525707088 175.36127333104315 340 180 C 351.3626474292912 184.63872666895685 384.982251230963 180.09504179548838 400 180 C 415.017748769037 179.90495820451162 411.43364250543914 184.2585594870033 410 190 C 408.56635749456086 195.7414405129967 410 202.87072025649834 410 210
I think the issue is the control points are so close together that it while it is technically perpendicular, it comes in very steep so you can only see it if you zoom way in.
Fundamentally, I think the problem to all the issues is that we don't really want to go through the same points as the metro router. By definition, that's going to make the curve more 'wiggly' than needed.
So my question is - is there a way to programmatically "round" the curves to look more like the one at the top, while ensuring they avoid obstacles? I suspect this will require both new router and connector logic to ensure the curve works for any arbitrary layout, though perhaps that's not true.
Something more like this:

How to count small seeds of two different kinds in an image?

I have an image in which there are about 503 chia seeds and 4 black pepper seeds below:
I am trying to count both of them. I a familiar with counting one type of object using the size of the seed and divide it by the total area covered by them. This is a simple approach but it works just fine.
For the image above, I found the grain size using the thresholding approach and it turns out to be 197 units and by using the below code I can find the total number of seeds:
image = Image.open('./shapes/S__14155926.jpg')
arr = np.array(image)
nseeds = np.sum(arr[...,2] < 100) / 197
print(nseeds)
504.58468309859154
The number is in ballpark and a couple of wrong counts here in there is no issue. However, how do I find the output classified as 500 small seeds and 4 large seeds without having to train a CNN model. I don't wish to recognize the seeds, just detect and count.
Both seeds are very similar in colour, which makes thresholding a bit challenging. Perhaps, passing the seeds through a strainer would be easier, so you can use your technique in two different images. If the seeds were nicely separated from each other, you could give it a try on blob detection.
First of all I find the area of all seeds
img=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_bin = 255-cv2.threshold(copy, 127, 255, cv2.THRESH_BINARY)[1]
S=cv2.moments(img_bin)
S['m00']
and get that the area is equal 26607210.0 pixels
Then I cut of 5 seeds
and find their area 175185.0. Thus one small seed has 35037.0.
Since a big seed contains about 8 small seeds ( in the sane way as above) I get that
there are about 728 small seeds and 4 big seeds approximatelly

Change grid size of a netCDF file

Let's assume I have 2 netCDF data files with data for the same region (like South America, Africa, etc) but the different grid sizes as 0.5 degrees x 0.5 degrees and 1.0 degrees x 1.0 degrees in another.
I want to increase or decrease its grid size to a different value such as 0.25 x 0.25 or 1.0 x 1.0 so that I can use this easily for raster calculations and comparison, etc.
Is there a method to do this using any bash script, CDO, etc.
A sample data can be downloaded from here. https://www.dropbox.com/sh/0vdfn20p355st3i/AABKYO4do_raGHC34VnsXGPqa?dl
Can different methods be followed for this like bilinear interpolation or cubic interpolation?
This is quite easy with ArcGIS and other software but is there a way to do it for a big netCDF file with large datasets.
Assume that this is just a subset of the data. What I will be later converting is a whole set of yearly data.
The resulted file should be a .nc file with the changed grid size as defined by the user.
You can use cdo to remap grids, e.g. to a regular 1 degree grid you can use:
cdo remapcon,r360x180 input.nc output.nc
As well as conservative first order remapping (remapcon), other options are :
remapbil : bilinear interpolation
remapnn : nearest neighbour interpolation
remapcon2 : 2nd order conservative remapping
It is also possible to remap one file to the grid used in another if you prefer:
cdo remapcon,my_target_file.nc in.nc out.nc
EDIT 2021: new video available...
To answer the comment below asking about which method to use, for a full guide on these interpolation methods and the issues you have to look out for regarding subsampling when coarse graining data, you can refer to my "regridding and interpolation" video guide on youtube.
In general if you are interpolating from high resolution to low resolution ("coarse gridding") by more than a factor of 2 you don't want to use bilinear interpolation as it will essentially subsample the field. This is especially problematic for non-smooth, highly heterogeneous fields such as precipitation. In those cases I would always suggest to use a conservative method (remapcon or remapcon2). See my video guide for details.
Another tip for speed is that, if you are performing the same interpolation procedure on many input files with the same resolution, then you can calculate the interpolation weights once using genbil, gencon etc, and then do the remapping function using those in the loop over the file. This is much faster, as the generation of the weights is the slow part of remapcon
NCO's ncremap has a one-line solution as well. Consider regriding a.nc to be on the same grid as b.nc. We will name the answer c.nc (this is the regridded a.nc).
ncremap -d b.nc a.nc c.nc
To choose conservative instead of bilinear interpolation (the default), use -a:
ncremap -a conserve -d b.nc a.nc c.nc

Brightness equalization using PIL in Python

There are multiple grayscale images in a folder that I am trying to equalize the brightness. The enhancement has to be applied in all the images except the first which will be our reference image. Also, the change only happens in the brightness of the images and not contrast since I want to preserve all the other details. This means that there is only a simple shift of histogram and no widening. I was trying to use the PIL module to calculate the average brightness. This method is probably faster since I can do it without a numpy array conversion.
The code:
with open("Img_Locations.txt") as file: # Assume the file locations in a text file
img_files = file.read().splitlines()
file.close()
self.images = [[] for x in range(len(img_files))]
for i in range(len(img_files)):
images[i] = Image.open(img_files[i])
im = images[i].convert('L')
stat = ImageStat.Stat(im)
ref_pil_original = int(stat.mean[0])
enhancer = ImageEnhance.Brightness(im)
enhanced_im = enhancer.enhance(1.5)
stat2 = ImageStat.Stat(enhanced_im)
ref_pil_bright = int(stat2.mean[0])
print(ref_pil_original, ref_pil_bright)
The sample output here was:
114 170
129 192
122 183
So the question is why does brightness enhancement when applied to the same factor of 1.5 yield me different differences for each image (basically how does the factor affect the pixels of my image)? If this is the case, what is the fastest way that I can adjust the brightness of my images by a constant factor to make sure that the final averages have the same mean value?
python -- measuring pixel brightness Here the PIL image is read by pixel. So does this mean that I will have to scan the image pixel by pixel to complete my requirement? I would like to know if this is indeed the way to do this since there are already existing functions. Unfortunately, I am aware of only PIL and OpenCV for image analysis and therefore the format of my read images are confined to them both.

Reducing / Enhancing known features in an image

I am microbiology student new to computer vision, so any help will be extremely appreciated.
This question involves microscope images that I am trying to analyze. The goal I am trying to accomplish is to count bacteria in an image but I need to pre-process the image first to enhance any bacteria that are not fluorescing very brightly. I have thought about using several different techniques like enhancing the contrast or sharpening the image but it isn't exactly what I need.
I want to reduce the noise(black spaces) to 0's on the RBG scale and enhance the green spaces. I originally was writing a for loop in OpenCV with threshold limits to change each pixel but I know that there is a better way.
Here is an example that I did in photo shop of the original image vs what I want.
Original Image and enhanced Image.
I need to learn to do this in a python environment so that I can automate this process. As I said I am new but I am familiar with python's OpenCV, mahotas, numpy etc. so I am not exactly attached to a particular package. I am also very new to these techniques so I am open to even if you just point me in the right direction.
Thanks!
You can have a look at histogram equalization. This would emphasize the green and reduce the black range. There is an OpenCV tutorial here. Afterwards you can experiment with different thresholding mechanisms that best yields the bacteria.
Use TensorFlow:
create your own dataset with images of bacteria and their positions stored in accompanying text files (the bigger the dataset the better).
Create a positive and negative set of images
update default TensorFlow example with your images
make sure you have a bunch of convolution layers.
train and test.
TensorFlow is perfect for such tasks and you don't need to worry about different intensity levels.
I initially tried histogram equalization but did not get the desired results. So I used adaptive threshold using the mean filter:
th = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 3, 2)
Then I applied the median filter:
median = cv2.medianBlur(th, 5)
Finally I applied morphological closing with the ellipse kernel:
k1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
dilate = cv2.morphologyEx(median, cv2.MORPH_CLOSE, k1, 3)
THIS PAGE will help you modify this result however you want.

Resources