OpenCV Tone Curve progrommatically - ios

I want to realize smth like tone curve.
I have predefined set of curves that I should apply to the image.
For instance:
as I understand on this chart we see dependences of current tone value to new, for example:
if we get first dot on the left - every r,g and b that = 0 will be converted to 64
or every value more than 224 will be converted to 0 and ect.
so I tried to change every pixel of image to new value
for test purpose i've simplified curve:
and here the code I have:
//init original image
cv::Mat originalMat = [self cvMatFromUIImage:inputImage];
//out image the same size
cv::Mat outMat = [self cvMatFromUIImage:inputImage];
//loop throw every row of image
for( int y = 0; y < originalMat.rows; y++ ){
//loop throw every column of image
for( int x = 0; x < originalMat.cols; x++ ){
//loop throw every color channel of image (R,G,B)
for( int c = 0; c < 3; c++ ){
if(originalMat.at<cv::Vec3b>(y,x)[c] <= 64)
outMat.at<cv::Vec3b>(y,x)[c] = 64 + ( originalMat.at<cv::Vec3b>(y,x)[c] ) -
( originalMat.at<cv::Vec3b>(y,x)[c] ) * 2 ;
if((originalMat.at<cv::Vec3b>(y,x)[c] > 64)&&(originalMat.at<cv::Vec3b>(y,x)[c] <= 128))
outMat.at<cv::Vec3b>(y,x)[c] = (( originalMat.at<cv::Vec3b>(y,x)[c] ) - 64 ) * 4
;
if((originalMat.at<cv::Vec3b>(y,x)[c] > 128))
outMat.at<cv::Vec3b>(y,x)[c] = ( originalMat.at<cv::Vec3b>(y,x)[c] ) + 128 -
(( originalMat.at<cv::Vec3b>(y,x)[c] ) - 128) * 3;
} //end of r,g,b loop
} //end of column loop
} //end of row loop
//send to output
return [self UIImageFromCVMat:outMat];
but here the result I get:
by some reason only 3/4 of image was processed
and it not matches with result i expected:
Update 0
thanks to #ACCurrent comment found errors in calculation(code and image updated), but still not understand why only 3/4 of images processed.
not sure that understand why 'noise' appears, hope it because of curve not smooth.
looks the way to avoid .at operation.
Update 1
original image:

You need to access the images with Vec4b
originalMat.type() is equals to 24
Your originalMat is of type 24, i.e. CV_8UC4. This means that the image has 4 channels, but you're accessing it with Vec3b as if it has only 3 channels. This explains why about 1/4 of the image is not modified.
So, simply replace every Vec3b in your code with Vec4b.

Related

how to assign 1D spectra to imagestack

any idea why this does not work?
DM::Image im2D = DM::RealImage("2D", 4, 2048);
DM::Image im3D= DM::RealImage("3D", 4, 2048, 9, 9);
PlugIn::ImageDataLocker im2D_LLl(im2D, PlugIn::ImageDataLocker::lock_data_CONTIGUOUS);
float *im2D_data = (float*)(im2D_LLl.get_image_data().get_data());
for (int i = 0; i <2048; i++) *Im2D_data++ = i;
Imaging::DataSlice planeSlice;
long xi=0, yi=0;
planeSlice = Imaging::DataSlice(Imaging::DataIndex(xi, yi, 0), Imaging::DataSlice::Slice1(2, 2048, 1));
DM::SliceImage(im3D, planeSlice) = im2D;
im3D is not changed, giving only zeros. In DM scripting side this would be:
slice1(im3D, 0,0,0,2,2048,1) = im2D
which works fine.
I'm somewhat confused by your example code.
It seems you create a 3D image of XYZ = 2048 x 9 x 9
but then slice it along dim=2 (z) for 2048 channels (it has only 9!)
The same is true for you script code. slice1 creates a 1D image along the dimension 2.
I think you've meant to use
slice2( img3D, 0,0,0, 0,9,1, 1,9,1 ) = img2d
Or, if you really meant to do spectrum-insertion (as you title suggests), you want some better named variables for sure.
Script example of creating a stack and filling it plane-wise:
image stack := realimage("Stack of 2048 2D images 9x9",4,9,9,2048)
for ( number i=0; i<2048; i++ ){
image plane2D := Realimage("2D plane 9x9",4,9,9)
plane2D = iradius + random()
stack.Slice2(0,0,i, 0,9,1, 1,9,1 ) = plane2D
}
stack.ShowImage()
Script example of creating a stack and filling it spectrum-wise:
image stack := realimage("Stack of 2048 2D images 9x9",4,9,9,2048)
for ( number i=0; i<9; i++ ){
for ( number j=0; j<9; j++ ){
image spec1D:= Realimage("1D spectrum 2048",4,2048)
spec1D = iradius + random()
stack.Slice1(i,j,0, 2,2048,1 ) = spec1D
}
}
stack.ShowImage()
As for the SDK code: When you create an image locker to change the data, make sure you use
im2d.DataChanged();
to finalize and update the image object.

Creating an image of difference of adjacent pixels with digitalmicrograph (DM) script

The following digitalmicrograph function tries to create an image by taking difference of neighboring pixel in a sub-row of a row of the image. The first pixel is replaced with a mean of the difference result of the sub-row thus created.
E.g. If the input image is 8 pixels wide and 1 rows tall and the sub-row size is 4 -
In_img = {8,9,2,4,9,8,7,5}
Then the output image will be -
Out_img = {mean(8,9,2,4)=5.75,9-8=1,2-9=-7,4-2=2,mean(9,8,7,5)=7.25,8-9=-1,7-8=-1,5-7=-2}
When I run this script, the first pixel of the first row is correct but rest of the pixels are incorrect. When I set the loop limit to only one sub-row and one row i.e. x=1 and y=1, then the script works correctly.
Any ideas as to what may be happening or what may be wrong with the script?
The test image is here and the result is here.
// Function to compute the standard deviation (sigma n-1) of an image, or
// a set of values passed in as pixel values in an image. The
// number of data points (n) the mean and the sum are also returned.
// version:20080229
// D. R. G. Mitchell, adminnospam#dmscripting.com (remove the nospam to make this email address work)
// v1.0, February 2008
void StandardDeviation(image arrayimg, number &stddev, number &n, number &mean, number &sum)
{
mean=mean(arrayimg)
number xsize, ysize
getsize(arrayimg,xsize, ysize)
n=xsize*ysize
sum=sum(arrayimg)
image imgsquared=arrayimg*arrayimg
number sumofvalssqrd=sum(imgsquared)
stddev=sqrt(((n*sumofvalssqrd)-(sum*sum))/(n*(n-1)))
}
image getVectorImage(image refImage, number rowsize)
{
number fh, fv, fhx
getsize(refImage, fh, fv)
fhx=trunc(fh/rowsize)
//result("ByteSize of refimage = "+refImage.ImageGetDataElementByteSize()+"\n")
//create image to save std of each row of the ref image.
//The std values are saved as pixels of one row. The row size is same as number of rows.
//use fhx*rowsize for the new imagesize as fhx is truncated value.
image retImage:=RealImage("",4,fhx*rowsize,fv)
image workImage=slice1(refImage,rowsize+1,0,0,0,rowsize-1,1)
number stddev,nopix,mean,sum
for ( number y=0;y<fv;y++)
{
for (number x=0;x<fhx;x++)
{
//result ("x,y="+x+","+y+"; fhx="+fhx+"; rowsize="+rowsize+"\n")
workImage=slice1(refImage,x*rowsize+1,y,0,0,rowsize-1,1)-slice1(refImage,x*rowsize,y,0,0,rowsize-1,1)
showimage(workImage)
StandardDeviation(workImage,stddev,nopix,mean,sum )
retImage[y,x*rowsize+1,y+1,x*rowsize+rowsize]=workImage
retImage[y,x]=mean
result("mean # row "+y+" = "+mean+"\n")
}
}
return retImage
}
showimage(getVectorImage(getfrontimage(),rowsize))
After your edit, I understood that you want to do something like this:
and that this should be performed for each line of the image individually.
The following script does this. (Explanations below.)
image Modify( image in, number subsize )
{
// Some checking
number sx,sy
in.GetSize(sx,sy)
if ( 0 != sx%subsize )
Throw( "The image width is not an integer multiplication of the subsize." )
// Do the means...
number nTile = sx/subsize
image meanImg := RealImage( "Means", 4, nTile , sy )
meanImg = 0
for ( number i=0; i<subsize; i++ )
meanImg += in.Slice2( i,0,0, 0,nTile,subsize, 1,sy,1 )
meanImg *= 1/subsize
// Do the shifted difference
image dif := RealImage( "Diff", 4, sx-1, sy )
dif = in.slice2( 1,0,0, 0,sx-1,1, 1,sy,1) - in.slice2( 0,0,0, 0,sx-1,1, 1,sy,1)
// Compile the result
image out := in.ImageClone()
out.SetName( in.getName() + "mod" )
out.slice2( 1,0,0, 0,sx-1,1, 1,sy,1 ) = dif
out.slice2( 0,0,0, 0,nTile,subsize, 1,sy,1 ) = meanImg
return out
}
number sx = 8, sy = 4
image img := RealImage( "test", 4, 8, 4 )
img = icol*10 + trunc( Random()*10 )
img.ShowImage()
Modify(img,4).ShowImage()
Some explanations:
You want to do two different things in the image, so you have to be careful not to overwrite data in pixels you will subsequently use for computation! Images are processed pixel by pixel, so if you first compute the mean and write it in the first pixel, the evaluation of the second pixel will be the difference of "9" and the just stored mean-value (not the original "8"). So you have to split computation and use "buffer" copies.
The slice2 command is extremely convenient, because it allows to define a stepsize when sampling. You can use it to address the dark-grey pixels directly.
Be aware of the difference between := and = in image expressions. The first is a memory assignment:
A := B means that A now is the same memory location as B. A is basically another name for B.
A = B means A gets the values of B (copied). A and B are two different memory locations and only values are copied over.
I have some observations in your script:
Instead of the defined method for getting mean/sum/stdev/n of an image, you can as easily get to those numbers from any image img using the following:
mean: number m = mean( img )
sum: number s = sum( img )
stdev: number sd = sqrt( variance( img ) )
pixels: number n = sum( 0 * img + 1 )
if you want to get the difference of an image with an image "shifted by one" you don't have to loop over the lines/columns but can directly use the slice2() command; a [ ] notation using icol and irow; or the command offset() Personally, I prefer the slice2() command.
If I want a script which gives me the standard deviation of the difference of each row with its successor row, i.e. stdDev( row_(y) - row_(y+1) ) for all y < sizeY, my script would be:
Image img := GetFrontImage()
number sx,sy
img.GetSize(sx,sy)
number dy = 1
Image dif = img.Slice2(0,0,0, 0,sx,1, 1,sy-1,1 ) - img.Slice2(0,dy,0, 0,sx,1, 1,sy-1,1)
Image sDevs := RealImage( "Row's stDev", 4, sy-1 )
for ( number y=0; y<sy-1; y++ )
sDevs[y,0] = SQRT( Variance( dif.Slice1(0,y,0, 0,sx,1) ) )
sDevs.ShowImage()
Is this, what you try to achieve? If not, please edit your question for some clarification.

Converting a gray scale raw format to bmp format

I'm trying to write a program which rearranges pixels from a gray scale raw file according to bmp format. But I think I would make some mistakes I don't know. Could someone tells me what's wrong with the following code? specs says a pixel for BMP format consist of 3 bytes. The first row on a raw image array is placed on the bottom row on a bmp pixel array, the second row is placed on the second bottom row, and so on. so I write the core code like, except GUI code and one for bmp header:
void MyClass_MakeBMP(void)
{
int i,j,k ;
/* m_uiWidth and m_uiHeight are rows and cols for the raw image, respectively. */
m_BMPheader.biWidth = m_uiWidth;
m_BMPheader.biHeight = m_uiHeight;
// raw format buffer -> 2d array.
// bmp format buffer -> 1d array.
// m_pcImgbuf is 1d array for a raw file.
// m_pcBMP is 2d array to be copied from m_pcImgbuf.
for(i = 0 ; i < m_uiHeight; i++)
{
k = -1; // index into m_pcImgbuf.
for(j = 0 ; j < m_uiWidth * 3; j++)
{
if( j % 3 == 0)
k++;
m_pcBMP[i * m_uiHeight + j] = m_pcImgbuf[(m_uiHeight - 1) - i][k];
}
}
}
Also, I don't care about padding, because this program can takes as input, 256*256, 128*128, and 512*512 images. Thank you in advance.
Your calculation for the destination offset is wrong.
m_pcBMP[i * 3*m_uiWidth + j] = ...

Move every pixel to right (by 1px) in OpenCV without using remap?

I want to move every pixel in an image to right by 1px, and below is the map I use to do the remap transformation.
This approach require much more time than it should to do such a simple transform. Is there a cv function I can use? Or do I just split the image into 2 images, one is src.cols-1 pixels wide, the other is 1 px wide, and then copy them to the new image?
void update_map()
{
for( int j = 0; j < src.cols; j++ ){
for( int i = 0; i < src.rows; i++ ){
if (j == src.cols-1)
mat_x_Rotate.at<float>(i,j) = 0;
else
mat_x_Rotate.at<float>(i,j) = j + 1;
mat_y_Rotate.at<float>(i,j) = i;
}
}
}
Things you can do to improve your performance:
remap is overkill for this purpose. It is more efficient to copy the pixels directly than to define an entire remap transformation and then use it.
switch your loop order: iterate over rows, then columns. (OpenCV's Mat is stored in row-major order, so iterating over columns first is very cache-unfriendly)
use Mat::ptr() to access pixels in the same row directly, as a C-style array. (this is a big performance win over using at<>(), which probably does stuff like check indices for each access)
take your if statement out of the inner loop, and handle column 0 separately.
As an alternative: yes, splitting the image into parts and copying to the new image might be about as efficient as copying directly, as described above.
Mat Shift_Image_to_Right( Mat src_in, int num_pixels)
{
Size sz_src_in = src_in.size();
Mat img_out(sz_src_in.height, sz_src_in.width, CV_8UC3);
Rect roi;
roi.x = 0;
roi.y = 0;
roi.width = sz_src_in.width-num_pixels;
roi.height = sz_src_in.height;
Mat crop;
crop = src_in(roi);
// Move the left boundary to the right
img_out = Scalar::all(0);
img_out.adjustROI(0, 0, -num_pixels, 0);
crop.copyTo(img_out);
img_out.adjustROI(0, 0, num_pixels, 0);
return img_out;
}

FFT Convolution - Really low PSNR

I'm convoluting an image (512*512) with a FFT filter (kernelsize=10), it looks good.
But when I compare it with an image which I convoluted the normal way the result was horrible.
The PSNR is about 35.
67,187/262,144 Pixel values have a difference of 1 or more(peak at ~8) (having a max pixel value of 255).
My question is, is it normal when convoluting in frequency space or might there be a problem with my convolution/transforming functions? . Because the strange thing is that I should get better results when using double as data-type. But it stays COMPLETELY the same.
When I transform an image into frequency space, DON'T convolute it, then transform it back it's fine and the PSNR is about 140 when using float.
Also, due to the pixel differences being only 1-10 I think I can rule out scaling errors
EDIT: More Details for bored interested people
I use the open source kissFFT library. With real 2dimensional input (kiss_fftndr.h)
My Image Datatype is PixelMatrix. Simply a matrix with alpha, red, green and blue values from 0.0 to 1.0 float
My kernel is also a PixelMatrix.
Here some snippets from the Convolution function
Used datatypes:
#define kiss_fft_scalar float
#define kiss_fft_cpx struct {
kiss_fft_scalar r;
kiss_fft_scalar i,
}
Configuration of the FFT:
//parameters to kiss_fftndr_alloc:
//1st param = array with the size of the 2 dimensions (in my case dim={width, height})
//2nd param = count of the dimensions (in my case 2)
//3rd param = 0 or 1 (forward or inverse FFT)
//4th and 5th params are not relevant
kiss_fftndr_cfg stf = kiss_fftndr_alloc(dim, 2, 0, 0, 0);
kiss_fftndr_cfg sti = kiss_fftndr_alloc(dim, 2, 1, 0, 0);
Padding and transforming the kernel:
I make a new array:
kiss_fft_scalar kernel[width*height];
I fill it with 0 in a loop.
Then I fill the middle of this array with the kernel I want to use.
So if I would use a 2*2 kernel with values 1/4, 1/4, 1/4 and 1/4 it would look like
0 0 0 0 0 0
0 1/4 1/4 0
0 1/4 1/4 0
0 0 0 0 0 0
The zeros are padded until they reach the size of the image.
Then I swap the quadrants of the image diagonally. It looks like:
1/4 0 0 1/4
0 0 0 0
0 0 0 0
1/4 0 0 1/4
now I transform it: kiss_fftndr(stf, floatKernel, outkernel);
outkernel is declarated as
kiss_fft_cpx outkernel= new kiss_fft_cpx[width*height]
Getting the colors into arrays:
kiss_fft_scalar *red = new kiss_fft_scalar[width*height];
kiss_fft_scalar *green = new kiss_fft_scalar[width*height];
kiss_fft-scalar *blue = new kiss_fft_scalar[width*height];
for(int i=0; i<height; i++) {
for(int j=0; i<width; j++) {
red[i*height+j] = input.get(j,i).getRed(); //input is the input image pixel matrix
green[i*height+j] = input.get(j,i).getGreen();
blue{i*height+j] = input.get(j,i).getBlue();
}
}
Then I transform the arrays:
kiss_fftndr(stf, red, outred);
kiss_fftndr(stf, green, outgreen);
kiss_fftndr(stf, blue, outblue); //the out-arrays are type kiss_fft_cpx*
The convolution:
What we have now:
3 transformed color arrays from type kiss_fft_cpx*
1 transformed kernel array from type kiss_fft_cpx*
They are both complex arrays
Now comes the convolution:
for(int m=0; m<til; m++) {
for(int n=0; n<til; n++) {
kiss_fft_scalar real = outcolor[m*til+n].r; //I do that for all 3 arrys in my code!
kiss_fft_scalar imag = outcolor[m*til+n].i; //so I have realred, realgreen, realblue
kiss_fft_scalar realMask = outkernel[m*til+n].r; // and imagred, imaggreen, etc.
kiss_fft_scalar imagMask = outkernel[m*til+n].i;
outcolor[m*til+n].r = real * realMask - imag * imagMask; //Same thing here in my code i
outcolor[m*til+n].i = real * imagMask + imag * realMask; //do it with all 3 colors
}
}
Now I transform them back:
kiss_fftndri(sti, outred, red);
kiss_fftndri(sti, outgreen, green);
kiss_fftndri(sti, outblue, blue);
and I create a new Pixel Matrix with the values from the color-arrays
PixelMatrix output;
for(int i=0; i<height; i++) {
for(int j=0; j<width; j++) {
Pixel p = new Pixel();
p.setRed( red[i*height+j] / (width*height) ); //I divide through (width*height) because of the scaling happening in the FFT;
p.setGreen( green[i*height+j] );
p.setBlue( blue[i*height+j] );
output.set(j , i , p);
}
}
Notes:
I already take care in advance that the image has a size with a power of 2 (256*256), (512*512) etc.
Examples:
kernelsize: 10
Input:
Output:
Output from normal convolution:
my console says :
142519 out of 262144 Pixels have a difference of 1 or more (maxRGB = 255)
PSNR: 32.006027221679688
MSE: 44.116752624511719
though for my eyes they look the same °.°
Maybe one person is bored and goes through the code. It's not urgent, but it's a kind of problem I just want to know what the hell I did wrong ^^
Last, but not least, my PSNR function, though I don't really think that's the problem :D
void calculateThePSNR(const PixelMatrix first, const PixelMatrix second, float* avgpsnr, float* avgmse) {
int height = first.getHeight();
int width = first.getWidth();
BMP firstOutput;
BMP secondOutput;
firstOutput.SetSize(width, height);
secondOutput.SetSize(width, height);
double rsum=0.0, gsum=0.0, bsum=0.0;
int count = 0;
int total = 0;
for(int i=0; i<height; i++) {
for(int j=0; j<width; j++) {
Pixel pixOne = first.get(j,i);
Pixel pixTwo = second.get(j,i);
double redOne = pixOne.getRed()*255;
double greenOne = pixOne.getGreen()*255;
double blueOne = pixOne.getBlue()*255;
double redTwo = pixTwo.getRed()*255;
double greenTwo = pixTwo.getGreen()*255;
double blueTwo = pixTwo.getBlue()*255;
firstOutput(j,i)->Red = redOne;
firstOutput(j,i)->Green = greenOne;
firstOutput(j,i)->Blue = blueOne;
secondOutput(j,i)->Red = redTwo;
secondOutput(j,i)->Green = greenTwo;
secondOutput(j,i)->Blue = blueTwo;
if((redOne-redTwo) > 1.0 || (redOne-redTwo) < -1.0) {
count++;
}
total++;
rsum += (redOne - redTwo) * (redOne - redTwo);
gsum += (greenOne - greenTwo) * (greenOne - greenTwo);
bsum += (blueOne - blueTwo) * (blueOne - blueTwo);
}
}
fprintf(stderr, "%d out of %d Pixels have a difference of 1 or more (maxRGB = 255)", count, total);
double rmse = rsum/(height*width);
double gmse = gsum/(height*width);
double bmse = bsum/(height*width);
double rpsnr = 20 * log10(255/sqrt(rmse));
double gpsnr = 20 * log10(255/sqrt(gmse));
double bpsnr = 20 * log10(255/sqrt(bmse));
firstOutput.WriteToFile("test.bmp");
secondOutput.WriteToFile("test2.bmp");
system("display test.bmp");
system("display test2.bmp");
*avgmse = (rmse + gmse + bmse)/3;
*avgpsnr = (rpsnr + gpsnr + bpsnr)/3;
}
Phonon had the right idea. Your images are shifted. If you shift your image by (1,1), then the MSE will be approximately zero (provided that you mask or crop the images accordingly). I confirmed this using the code (Python + OpenCV) below.
import cv
import sys
import math
def main():
fname1, fname2 = sys.argv[1:]
im1 = cv.LoadImage(fname1)
im2 = cv.LoadImage(fname2)
tmp = cv.CreateImage(cv.GetSize(im1), cv.IPL_DEPTH_8U, im1.nChannels)
cv.AbsDiff(im1, im2, tmp)
cv.Mul(tmp, tmp, tmp)
mse = cv.Avg(tmp)
print 'MSE:', mse
psnr = [ 10*math.log(255**2/m, 10) for m in mse[:-1] ]
print 'PSNR:', psnr
if __name__ == '__main__':
main()
Output:
MSE: (0.027584912741602553, 0.026742391458366047, 0.028147870144492403, 0.0)
PSNR: [63.724087463606452, 63.858801190963192, 63.636348220531396]
My advice for you to try to implement the following code :
A=double(inputS(1:10:length(inputS))); %segmentation
A(:)=-A(:);
%process the image or signal by fast fourior transformation and inverse fft
fresult=fft(inputS);
fresult(1:round(length(inputS)*2/fs))=0;
fresult(end-round(length(fresult)*2/fs):end)=0;
Y=real(ifft(fresult));
that's code help you to obtain the same size image and good for remove DC component ,the you can to convolution.

Resources