Conversion of arbitrary range to luma - image-processing

I have 2d matrix of values in range <-a, b>. I would like to visualize this image by grayscale image. How I should process my data to visualize it correctly?
As far as I know human eye has logarithmic scale, so my transformation should be logarithmic too.

Convert your values to the luma in a perceptually uniform color space, for example CIE Lab or Luv. Then convert from that to RGB for display.
These are available in the colormath module, for example.
If your input value is in x
L = 100*(x - xmin) / (xmax - xmin) # L is 0-100
a, b = 0, 0 # neutral values
from colormath.color_objects import LabColor, RGBColor
from colormath.color_conversions import convert_color
lab = LabColor(L, a, b)
rgb = convert_color(lab, RGBColor)
# display rgb
Matplotlib has a lot of info about this in the section on colormaps:. https://matplotlib.org/users/colormaps.html

Related

Calculate standard deviation for grayscale imagenet pixel values with rotation matrix and regular imagenet standard deviation

I want to train some models to work with grayscale images, which e.g. is useful for microscope applications (Source). Therefore I want to train my model on graysale imagenet, using the pytorch grayscale conversion (torchvision.transforms.Grayscale), to convert the RGB imagenet to a grayscale imagenet. Internally pytorch rotates the color space from RGB to YPbPr as follows:
Y' is the grayscale channel then, so that Pb and Pr can be neglected after transformation. Actually pytorch even only calculates
grayscale = (0.2989 * r + 0.587 * g + 0.114 * b)
To normalize the image data, I need to know grayscale-imagenet's mean pixel value, as well as the standard deviation. Is it possible to calculate those?
I had success in calculating the mean pixel intensity using
meanGrayscale = 0.2989 * r.mean() + 0.587 * g.mean() + 0.114 * b.mean()
Transforming an image and then calculating the grayscale mean, gives the same result as first calculating the RGB means and then transforming those to a grayscale mean.
However, I am clueless when it comes to calculating the variance or standard deviation now. Does somebody have any idea, or knows some good literature on the topic? Is this even possible?
I found a publication "Jianxin Gong - Clarifying the Standard Deviational Ellipse" ... There he does it in 2 dimensions (as far as I understand). I just could not figure out yet how to do it in 3D.
Okay, I wasn't able to calculate the standard deviation as planned, but did it using the code below. The grayscale imagenet's train dataset mean and standard deviation are (round it as much as you like):
Mean: 0.44531356896770125
Standard Deviation: 0.2692461874154524
import multiprocessing
import os
def calcSTD(d):
meanValue = 0.44531356896770125
squaredError = 0
numberOfPixels = 0
for f in os.listdir("/home/imagenet/ILSVRC/Data/CLS-LOC/train/"+str(d)+"/"):
if f.endswith(".JPEG"):
image = imread("/home/imagenet/ILSVRC/Data/CLS-LOC/train/"+str(d)+"/"+str(f))
###Transform to gray if not already gray anyways
if np.array(image).ndim == 3:
matrix = np.array(image)
blue = matrix[:,:,0]/255
green = matrix[:,:,1]/255
red = matrix[:,:,2]/255
gray = (0.2989 * red + 0.587 * green + 0.114 * blue)
else:
gray = np.array(image)/255
###----------------------------------------------------
for line in gray:
for pixel in line:
squaredError += (pixel-meanValue)**2
numberOfPixels += 1
return (squaredError, numberOfPixels)
a_pool = multiprocessing.Pool()
folders = []
[folders.append(f.name) for f in os.scandir("/home/imagenet/ILSVRC/Data/CLS-LOC/train") if f.is_dir()]
resultStD = a_pool.map(calcSTD, folders)
StD = (sum([intensity[0] for intensity in resultStD])/sum([pixels[1] for pixels in resultStD]))**0.5
print(StD)
During the process some errors like this occured:
/opt/conda/lib/python3.7/site-packages/PIL/TiffImagePlugin.py:771:
UserWarning: Possibly corrupt EXIF data. Expecting to read 8 bytes
but only got 4. Skipping tag 41486 "Possibly corrupt EXIF data. "
The repective images from the 2019 version of ImageNet were skipped.

OpenCV: Set brightness of each RGB pixel so brightest channel (R, G, or B) is at maximum

For each pixel in an RGB image, I want to increase its brightness so that the strongest channel of that pixel (R, G, or B) is maximized. For example, if a pixel has an RGB value of (128, 64, 32), and the maximum channel value is 255, then that pixel should be changed to approximately (255, 128, 64). This is sort of a poor man's shadow removal system.
I can do this by iterating the pixels explicitly, but that is very inefficient. What is the most efficient way to do this by using strictly OpenCV methods? It seems it might use YUV space?
(By the way, I am using C# with EmguCV, but a straight Python/OpenCV answer would be fine. EDIT: But I can't use Python libraries)
In Python, OpenCV images are just numpy array. So here's a python/numpy approach:
# toy sample
np.random.seed(1)
a = np.random.randint(0, 100, (4,4,3), dtype=np.uint8)
# get max values across channels, scaled by 255
maxx = np.max(a, axis=-1)/255
# scale a by maxx
a = (a / maxx[:,:, None]).astype(np.uint8)
Input (with plt.imshow() so in rgb):
Output:

Fastest way to apply color matrix to RGB image using OpenCV 3.0?

I have a color image represented as an OpenCV Mat object (C++, image type CV_32FC3). I have a color correction matrix that I want to apply to each pixel of the RGB color image (or BGR using OpenCV convention, doesn't matter here). The color correction matrix is 3x3.
I could easily iterate over the pixels and create a vector v (3x1) representing RGB, and then compute M*v, but this would be too slow for my real-time video application.
The cv::cvtColor function is fast, but does not seem to allow for custom color transformations.
http://docs.opencv.org/2.4/modules/imgproc/doc/miscellaneous_transformations.html#cvtcolor
Similar to the following, but I am using OpenCV for C++, not Python.
Apply transformation matrix to pixels in OpenCV image
Here's the code that worked using cv::reshape. It was fast enough for my application:
#define WIDTH 2048
#define HEIGHT 2048
...
Mat orig_img = Mat(HEIGHT, WIDTH, CV_32FC3);
//put some data in orig_img somehow ...
/*The color matrix
Red:RGB; Green:RGB; Blue:RGB
1.8786 -0.8786 0.0061
-0.2277 1.5779 -0.3313
0.0393 -0.6964 1.6321
*/
float m[3][3] = {{1.6321, -0.6964, 0.0393},
{-0.3313, 1.5779, -0.2277},
{0.0061, -0.8786, 1.8786 }};
Mat M = Mat(3, 3, CV_32FC1, m).t();
Mat orig_img_linear = orig_img.reshape(1, HEIGHT*WIDTH);
Mat color_matrixed_linear = orig_img_linear*M;
Mat final_color_matrixed = color_matrixed_linear.reshape(3, HEIGHT);
A few things to note from the above: The color matrix in the comment block is the one I would ordinarily apply to an RGB image. In defining the float array m, I switched rows 1 and 3, and columns 1 and 3 for OpenCV's BGR ordering. The color matrix also must be transposed. Usually a color matrix is applied as M* v = v_new, where M is 3x3 and v is 3x1 but here we are doing vT *MT = v_newT to avoid having to transpose each 3-channel pixel.
Basically the linked answer uses reshape to convert your CV_32FC3 mat of size m x n to a CV_32F mat of size (mn) x 3. After that, each row of the matrix contains exactly color channels of one pixel. You can then apply usual matrix multiplication to obtain a new mat and reshape it back to the original shape with three channels.
Note: It may be worth noticing that the default color space of opencv is BGR, not RGB.

Is the colormap conversion of openCV linear?

I have a gray scale image (i.e. 1 channel). I am using the opencv function applyColorMap() to improve the visualization. For example, if I have the code below
applyColorMap(imgGray, imgOut, COLORMAP_RAINBOW);
Somebody does know if the colormap conversion is linear?
(source: opencv.org)
Colormaps used by the opencv function: applyColorMap have a linear relation between them. They are based on GNU Octave / Matlab colormaps
Graphics functions that use pseudocolor - mesh, surf, pcolor, and others - map a color matrix, c, whose values are in the range [cmin, cmax], to an array of indices, k, in the range [1, m]. The values of cmin and cmax are either min(min(c)) and max(max(c)), or are specified by caxis. The mapping is linear, with cmin mapping to index 1 and cmax mapping to index m. The indices are then used with the colormap to determine the color associated with each matrix element. reference

Noise Estimation / Noise Measurement in Image

I want to estimate the noise in an image.
Let's assume the model of an Image + White Noise.
Now I want to estimate the Noise Variance.
My method is to calculate the Local Variance (3*3 up to 21*21 Blocks) of the image and then find areas where the Local Variance is fairly constant (By calculating the Local Variance of the Local Variance Matrix).
I assume those areas are "Flat" hence the Variance is almost "Pure" noise.
Yet I don't get constant results.
Is there a better way?
Thanks.
P.S.
I can't assume anything about the Image but the independent noise (Which isn't true for real image yet let's assume it).
You can use the following method to estimate the noise variance (this implementation works for grayscale images only):
def estimate_noise(I):
H, W = I.shape
M = [[1, -2, 1],
[-2, 4, -2],
[1, -2, 1]]
sigma = np.sum(np.sum(np.absolute(convolve2d(I, M))))
sigma = sigma * math.sqrt(0.5 * math.pi) / (6 * (W-2) * (H-2))
return sigma
Reference: J. Immerkær, “Fast Noise Variance Estimation”, Computer Vision and Image Understanding, Vol. 64, No. 2, pp. 300-302, Sep. 1996 [PDF]
The problem of characterizing signal from noise is not easy. From your question, a first try would be to characterize second order statistics: natural images are known to have pixel to pixel correlations that are -by definition- not present in white noise.
In Fourier space the correlation corresponds to the energy spectrum. It is known that for natural images, it decreases as 1/f^2 . To quantify noise, I would therefore recommend to compute the correlation coefficient of the spectrum of your image with both hypothesis (flat and 1/f^2), so that you extract the coefficient.
Some functions to start you up:
import numpy
def get_grids(N_X, N_Y):
from numpy import mgrid
return mgrid[-1:1:1j*N_X, -1:1:1j*N_Y]
def frequency_radius(fx, fy):
R2 = fx**2 + fy**2
(N_X, N_Y) = fx.shape
R2[N_X/2, N_Y/2]= numpy.inf
return numpy.sqrt(R2)
def enveloppe_color(fx, fy, alpha=1.0):
# 0.0, 0.5, 1.0, 2.0 are resp. white, pink, red, brown noise
# (see http://en.wikipedia.org/wiki/1/f_noise )
# enveloppe
return 1. / frequency_radius(fx, fy)**alpha #
import scipy
image = scipy.lena()
N_X, N_Y = image.shape
fx, fy = get_grids(N_X, N_Y)
pink_spectrum = enveloppe_color(fx, fy)
from scipy.fftpack import fft2
power_spectrum = numpy.abs(fft2(image))**2
I recommend this wonderful paper for more details.
Scikit Image has an estimate sigma function that works pretty well:
http://scikit-image.org/docs/dev/api/skimage.restoration.html#skimage.restoration.estimate_sigma
it also works with color images, you just need to set multichannel=True and average_sigmas=True:
import cv2
from skimage.restoration import estimate_sigma
def estimate_noise(image_path):
img = cv2.imread(image_path)
return estimate_sigma(img, multichannel=True, average_sigmas=True)
High numbers mean low noise.

Resources