I would like to do cumulative sum along x or y direction of the image data.
Is there any function in DM-scripting like "cumsum" in Matlib?
Thanks!
for example an image of 4x4 pixels the pixel values are
1 2 3 4
2 3 4 5
3 4 5 6
4 5 6 7
cumulative sum along x direction will result in:
1 1+2=3 1+2+3=6 1+2+3+4=10
2 5 9 14
3 7 12 18
4 9 15 22
There are differnt ways to achieve this, but potentially the fastest and easiest is to create a "fully binned" version of the image.
image img := GetFrontImage()
number sizeX, sizeY
img.GetSize( sizeX, sizeY )
image vSum = Rebin( img, 1, sizeY )
image hSum = Rebin( img, sizeX, 1 )
vSum.SetName( "vertical sum" )
vSum.ShowImage()
hSum.SetName( "horizontal sum" )
hSum.ShowImage()
If you want a 2D image as a result, where each pixel holds the sum of all its pixels to the left, you can do this by adding up offset images:
image img := GetFrontImage()
number sizeX, sizeY
img.GetSize( sizeX, sizeY )
image vCumSum := img.ImageClone()
for( number x = 1; x<sizeX ; x++ )
{
hCumSum += offset( img, -x, 0 )
}
hCumSum.SetName( "horizontal sum (cumulative)" )
hCumSum.ShowImage()
Alternatively, you can create an expression using intrinsic variables as in
image img := GetFrontImage()
image hCumSum := 0 * img.ImageClone()
hCumSum += img[icol,irow] + hCumSum[ icol - 1, irow ]
hCumSum.SetName( "horizontal sum (cumulative)" )
hCumSum.ShowImage()
GMS 3.4 also offers a dedicated, speed optimized command:
RealImage Project( BasicImage img, Number axis )
RealImage Project( BasicImage img, Number axis, Boolean rescale )
void Project( BasicImage img, BasicImage dst, Number axis )
void Project( BasicImage img, BasicImage dst, Number axis, Boolean rescale )
Another way to do projection is by matrix multiplication. multiply a 2-D image by a 1-D matrix of 1's will project the image onto 1-D accumulation.
number d0, d1
image HProject, VProject, ones, img
img:=getfrontImage()
img.getSize(d0,d1)
ones:=exprSize(1,d0,1)
HProject=MatrixMultiply(img,ones)
HProject.rotateLeft()
HProject.showImage()
ones:=exprSize(d1,1,1)
VProject=MatrixMultiply(ones,img)
VProject.showImage()
I also have one
image cumsum(image img)
// computes the cumulative sum along x direction
{
number sx, sy
img.GetSize(sx,sy)
for(number i=1; i<sx; i++)
{
img[0,i,sy,i+1]=img[0,i-1,sy,i]+img[0,i,sy,i+1]
}
return img
}
image im=getfrontimage()
im=im.cumsum()
im.showimage()
Related
I would like to draw a red rectangle using the following function on a YUV420P frame. Following code alters the frame and I can see two black line(top and bottom) remaining black dots scattered. Any suggestions?
void draw_rectangle(uint8_t *frame, int x, int y,
int width, int height,
int img_width, int img_height)
{
cv::Mat frame_yuv;
int size[2];
Point pt1, pt2;
cv::Scalar color = Scalar(255, 0, 0);
size[0] = img_width + img_width/2;
size[1] = img_height;
frame_yuv = cv::Mat(2, size, CV_8UC1, frame);
pt1.x = x;
pt1.y = y;
pt2.x = x + width;
pt2.y = y + height;
rectangle(frame_yuv, pt1, pt2, Scalar(0, 0, 255));
}
Finally, I got my code working. Steps are given below for reference.
frame_yuv = cv::Mat(2, size, CV_8UC3, frame);
cv::Mat C(2,2, CV_8UC3, color);
cv::Mat C_yuv;
cvtColor(C, C_yuv, cv::COLOR_BGR2YUV_I420);
// Set the R, G, B values to C_yuv
// Extract the Y, U, V components to separate Mat's
// Apply rectange first on Y component
// Devide each points pt1, pt2 by 2
// Apply the rectange on U, V
No extra copy of the frame is done.
As you haven't provided any sample data, please use the file kindly provided by #zindarod with dimensions 144x176.
Here is how the YUV data look in memory:
Notice in the stream along the bottom... all the Y pixels come first. Then all the U pixels but downsampled by a factor of 4. Then all the V pixels, also downsampled by a factor of 4.
I haven't got time to write the code in OpenCV, but I can show you how to make a regular Mat out of it.
Step 1 - Extract Y channel
Take the first 144x176 bytes and put them into an 144x176 8UC1 Mat called Y.
Step 2 - Extract U channel
Skip the first 144x176 bytes and then take the next 72x88 bytes and put them into another 72x88 8UC1 Mat called U. Resize this Mat to double the width and double the height, i.e. 144x176.
Step 3 - Extract the V channel
Skip the first (144x176) + (88x72) bytes and then take the next 72x88 bytes and put them into another 72x88 8UC1 Mat called V. Resize this Mat to double the width and double the height, i.e. 144x176.
Step 4 - Merge
Take the Y, U, and V Mats and merge them into an 8UC3 Mat:
// Now merge the 3 individual channels into 3-band bad boy
auto channels = std::vector<cv::Mat>{Y, U, V};
cv::Mat ThreeBandBoy;
cv::merge(channels, ThreeBandBoy);
There is some code here that does more or less exactly what is needed for Steps 1-3.
I am reading this YUV image from file, which is YUV_I420.
fstream file;
file.open("yuv_i420.yuv", ios::in | ios::binary);
// size of image in RGB
size_t rows = 144, cols = 176;
if (!file.is_open())
stderr<<"Error opening file"<<endl;
else {
// get total size of file
auto size = file.tellg();
file.seekg(0,ios::end);
size = file.tellg() - size;
file.seekg(0,ios::beg);
char *buffer = new char[size];
// read image from file
if (file.read(buffer, size)) {
// create YUV Mat
Mat yuv_I420(rows + rows / 2, cols, CV_8UC1, buffer);
// draw a rectangle on YUV image, keep in mind that the YUV image is a
// single channel grayscale image, size is different than the BGR image
rectangle(yuv_I420, Point(10, 10), Point(50, 50), Scalar(255));
// convert to BGR to check validity
Mat bgr;
cvtColor(yuv_I420, bgr, cv::COLOR_YUV2BGR_I420);
cv::imshow("image", bgr);
cv::waitKey(0);
}
file.close();
delete[] buffer;
}
I am trying to find that whether there is any rectangle/square present inside my area of interest. Here is what I have achieved till now.
Below is the region of interest which I snipped out of the original image using JavaCV.
Mat areaOfInterest = OpenCVUtils.getRegionOfInterest("image.jpg",295,200,23,25);
public static Mat getRegionOfInterest(String filePath , int x, int y, int width, int height){
Mat roi = null;
try{
Mat image = Imgcodecs.imread(filePath);
Rect region_of_interest= new Rect(x,y,width,height);
roi = image.submat(region_of_interest);
}catch (Exception ex){
}
return roi;
}
Now I'm trying to find whether there is any rectangle present in the area of interest. I have used following lines of code to detect that as well.
Mat gray = new Mat();
Mat binary = new Mat();
Mat hierarchy = new Mat();
ArrayList<MatOfPoint> contours = new ArrayList<>();
cvtColor(image,gray,COLOR_BGR2GRAY);
Core.bitwise_not(gray,binary);
findContours(binary,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_NONE);
if(contours.size() > 0){
for (MatOfPoint contour:contours) {
Rect rect = boundingRect(contour);
/// x = 0, y = 1 , w = 2, h =3
Point p1 = new Point(rect.x,rect.y);
Point p2 = new Point(rect.width + rect.x, rect.height+rect.y);
rectangle(image,p1,p2,new Scalar(0,0,255));
Imgcodecs.imwrite("F:\\rect.png",image);
}
}
But instead of finding the the square inside the image it is outlining the parts of the image as following.
It would be great if someone pushes me in the right direction.
OpenCV's findContours() treats the input image as binary, where everything that is 0 is black, and any pixel >0 is white. Since you're reading a jpg image, the compression makes it so that most white pixels aren't exactly white, and most black pixels aren't exactly black. Thus, if you have an input image like:
3 4 252 250 3 1
3 3 247 250 3 2
3 2 250 250 2 2
4 4 252 250 3 1
3 3 247 250 3 2
3 2 250 250 2 2
then findContours() will just outline the whole thing, since to it it's equivalent to all being 255 (they're all > 0).
All you need to do is binarize the image with something like threshold() or inRange(), so that your image actually comes out to
0 0 255 255 0 0
0 0 255 255 0 0
0 0 255 255 0 0
0 0 255 255 0 0
0 0 255 255 0 0
0 0 255 255 0 0
Then you'd correctly get the outline of the 255 block in the center.
After making a circular ROI in an image, how can I get the information (average, standar deviation, variance) from that image region using script?
Can I link the position in the ciruclar ROI with original image?
This task is unfortunately not as straight forward and easy as one would hope.
While scripting supports a convenient shortcut to restrict image operations to rectangular ROIs ( using the img[] notation ), there is nothing like that for irregular ROIs.
In such a case, one has to manually create a binary mask of a ROI and perform the wanted operations manually. The example script at the bottom of this post shows how the average value of an irregular ROI may be computed.
CreateImageWithROI() Creates a test image with two ROIs on it
GetFirstIrregularROIOfImage() just returns the first found, irregular ROI of an image
GetROIMean() is the actual example
The command ROIAddToMask() is used to create the mask. Note, that there is also a similar command which would perform the action with all ROIs of an image display at once: ImageDisplayAccumulateROIsToMask()
So far, so good.
However, it turns out that the newly introduced Circular ROIs do not yet support the mask-creation commands correctly (Tested with GMS 3.1).
Instead, they always use the bounding rectangle of the ROI:
It is therefore necessary to go even one step back and read the ROI's coordinates to create a mask from it manually. Get the ROI's bounding-box and create a mask using an icol and irow expression for an ellipse. In the example below:
GetFirstOvalROIOfImage() just returns the first found, oval ROI of an image
MyAddOvalROIToMask() is the manual mask creation for oval ROIs
Example code:
image CreateImageWithROI()
{
// Create and show image
number sx = 256, sy = 256
image img := RealImage( "Image", 4, sx, sy )
img = sin( 0.1 * iradius ) * cos( 7 * itheta )
img.ShowImage()
// Create an irregular, closed ROI
ROI myIrRoi = NewROI()
myIrRoi.ROIAddVertex( 0.3 * sx, 0.1 * sy )
myIrRoi.ROIAddVertex( 0.7 * sx, 0.2 * sy )
myIrRoi.ROIAddVertex( 0.5 * sx, 0.6 * sy )
myIrRoi.ROIAddVertex( 0.1 * sx, 0.8 * sy )
myIrRoi.ROISetIsClosed(1)
myIRRoi.ROISetVolatile(0)
// Create an oval ROI
ROI myOvalROI = NewROI()
myOvalROI.ROISetOval( 0.7 * sy, 0.7 * sx, 0.9 * sy, 0.8 * sx )
myOvalROI.ROISetVolatile(0)
// AddROIs
imageDisplay disp = img.ImageGetImageDisplay( 0 )
disp.ImageDisplayAddROI( myIRRoi )
disp.ImageDisplayAddROI( myOvalROI )
return img
}
ROI GetFirstIrregularROIOfImage( image img )
{
if ( img.ImageIsValid() )
{
if ( 0 != img.ImageCountImageDisplays() )
{
imageDisplay disp = img.ImageGetImageDisplay( 0 )
number nRois = disp.ImageDisplayCountROIs()
for ( number i = 0; i < nRois; i++ )
{
ROI testROI = disp.ImageDisplayGetRoi( i )
number isIrregularClosed = 1
isIrregularClosed *= testROI.ROIIsClosed();
isIrregularClosed *= !testROI.ROIIsOval();
isIrregularClosed *= !testROI.ROIIsRectangle();
isIrregularClosed *= ( 2 < testROI.ROICountVertices());
if ( isIrregularClosed )
return testROI
}
}
}
Throw( "No irregular ROI found" )
}
ROI GetFirstOvalROIOfImage( image img )
{
if ( img.ImageIsValid() )
{
if ( 0 != img.ImageCountImageDisplays() )
{
imageDisplay disp = img.ImageGetImageDisplay( 0 )
number nRois = disp.ImageDisplayCountROIs()
for ( number i = 0; i < nRois; i++ )
{
ROI testROI = disp.ImageDisplayGetRoi( i )
if ( testROI.ROIIsOval() )
return testROI
}
}
}
Throw( "No oval ROI found" )
}
void MyAddOvalROIToMask( image img, ROI ovalROI )
{
number top, left, bottom, right
ovalROI.ROIGetOval( top, left, bottom, right )
number sx = ( right - left )
number sy = ( bottom - top )
number cx = sx/2 // Used as both center x coordiante and x radius!
number cy = sy/2 // Used as both center y coordiante and y radius!
// Create mask of just the rect area
image maskCut := RealImage( "", 4, sx, sy )
maskCut = ( ((cx-icol)/cx)**2 + ((cy-irow)/cy)**2 <= 1 ) ? 1 : 0
// Apply mask to image
img[top, left, bottom, right] = maskCut
}
number GetROIMean( image img, ROI theRoi )
{
if ( !img.ImageIsValid() ) Throw( "Invalid image in GetROIMean()" )
if ( !theRoi.ROIIsValid() ) Throw( "Invalid roi in GetROIMean()" )
// Create a binary mask of "img" size using the ROI's coordinates
image mask = img * 0; // image of same size as "img" with 0 values
number sx, sy
img.GetSize( sx, sy )
// Oval ROIs are not supported by the command correctly
// Hence check and compute mask manually..
if ( theROI.ROIIsOval() )
MyAddOvalROIToMask( mask, theROI )
else
theROI.ROIAddToMask( mask, 0, 0, sx, sy )
if ( TwoButtonDialog( "Show mask?", "Yes", "No" ) )
mask.ShowImage()
// Do meanValue as sums of masked points
number maskedPoints = sum( mask )
number maskedSum
if ( 0 < maskedPoints )
maskedSum = sum( mask * img ) / maskedPoints
else
maskedSum = sum( img )
return maskedSum
}
Result( "\n Testing irregular and oval ROIs on image.\n" )
image testImg := CreateImageWithROI()
ROI testROIir = GetFirstIrregularROIOfImage( testImg )
number ROIirMean = GetROIMean( testImg, testROIir )
Result( "\n Mean value (irregular ROI): "+ ROIirMean )
ROI testROIoval = GetFirstOvalROIOfImage( testImg )
number ROIovalMean = GetROIMean( testImg, testROIoval )
Result( "\n Mean value (oval ROI) : "+ ROIovalMean )
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.
I got the reference image of a video(.avi) so the the width and height of the image must be as the same as the width and height of the video is and it is.
(my video is a CvCapture* and my image is a IplImage*)
Width is 1280 and height is 960;
But when I told OpenCV that if the coordinate of a pixel is in the specific rectangle then do something. All of the width of the image was the width of that rectangle.
const int Y1 = 430, Y2 = 730, X1 = 0, X2 = 1279 ;
for (int i = Y1; i <= Y2; i++)
for (int j = X1; j <= X2; j++)
CV_IMAGE_ELEM(frame_BGR, uchar, i, j) = 255;
But only near 1/5 of the width of the page is now white! Then I X2 = 3000. Then all of the width of the image is now white and silly thing is that when I change X2 = 10000 then code didn't report SEGMENTATION FAULT.
Why the width-reporting is not working correctly?
I run it on both Ubuntu - g++ and and Windows 7 - visual studio 2010. I think my resolution is high. I know that the video is taken by a Nokia 5800 cellphone. It is so important for me, so excuse me if I was very specific!
If the image isn't single channel , you are using CV_IMAGE_ELEM wrongly
It has to be pixel = CV_IMAGE_ELEM( frame_BGR, uchar, row_number, col_number * 3 + color_channel );
So for BGR:
uchar blue = CV_IMAGE_ELEM( frame_BGR, uchar, row_number, col_number * 3 + 0 );
uchar green = CV_IMAGE_ELEM( frame_BGR, uchar, row_number, col_number * 3 + 1 );
uchar red = CV_IMAGE_ELEM( frame_BGR, uchar, row_number, col_number * 3 + 2);
Really CV_IMAGE_ELEM isn't really worth the effort, you might as well just use frame_BGR.ptr(row) to get a pointer to the start of the row and then increment the pointer to give you B,G,R along the row.