What is the best way to extract the part with pattern from binary images like these? Size and position of pattern may vary a bit from image to image.
I've tried morphologyEx, but it looses too much info (and pattern position/size)
How do detect too noisy images?
Thanks in advance.
UPD
Looks like it works now. Still dont know how to detect 'bad' noisy frames. Here is an example with an excellent frame.
orig img (240x320).
(0)-> Morph close 4x4, to find large black areas.
(0) -> Morph open 2x2 -> Morph close 2x2 -> set black where black in (1).
(2) -> gaussian blur 13x13.
(3) -> gray to bin and leave only white zones with area more than some value.
When all the frames are processed 1-4:
for (var df in dFrames) {
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++) {
if (df.frame[x][y] == 0xffffffff)
screenImg[x][y] += (int)(pow(df.whiteArea, 1.2) / maxWhiteAreaInAllDfs * 100);
}
}
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++) {
if (screenImg[x][y] > dFrames.length * 0.5 * (100/2).floor())
screenImg[x][y] = 0xffffffff;
}
Finally crop all the frames (4), wrap perspective, find pattern width/height and calc avg color in every 'square'.
Related
I'm trying to properly detect the edges of a playing card that has been blurred, grayscaled and then thresholded. I thought having the sharp black and white contrast would make the edges quite easy to detect, but no joy so far. I'm starting with:
And using the Canny Edge Detector I wrote producing this:
The result of Sobel was basically the same. However using OpenCV's Canny Detection I could produce this:
The border being correctly fitted together is what I'm desperately needing to recreate in my own code, and I'm not committed to using any particular type of Edge Detection, I just need to find an algorithm that will give me the connected edge! My Canny code can be found here, and it is very much based from the LIRE code here. If anybody could help me go from the first image to the third I would be incredibly grateful! Any edge detection welcome!
Edit: Code for NMS:
//main program
for(int x = 1; x < width-1; x++)
{
for(int y = 1; y < height-1; y++)
{
if(src.getRaster().getPixel(x, y, tmp)[0] >= 250)
{
trackWeakOnes(x, y, src);
}
}
}
private static void trackWeakOnes(int x, int y, BufferedImage src)
{
for (int a = x - 1; a <= x + 1; a++)
{
for (int b = y - 1; b <= y + 1; b++)
{
if (checkWeak(a, b, src))
{
src.getRaster().setPixel(x, y, tmpMax);
trackWeakOnes(a, b, src);
}
}
}
}
private static boolean checkWeak(int x, int y, BufferedImage src)
{
return ((src.getRaster().getPixel(x, y, tmpPix)[0] > 0) &&
(src.getRaster().getPixel(x, y, tmpPix)[0] < 255));
}
tmpPix is an empty array to be filled, tmpMax is an array {255, 255, 255} to make edges white.
For this clean image, you don't need complex algorithms. A couple of simple filters will do the trick.
In matlab, the code looks like:
O=abs(filter2([-1 0 1],I))+abs(filter2([-1;0;1],I));
which means that for each pixel (x,y) you do:
output(x,y) = abs( I(x+1,y)-I(x-1,y) ) + abs( I(x,y+1) - I(x,y-1) );
I didn't read your code, but I observe a strange artifact: along the horizontal edges, the detected pixels come in isolated 8-connected triples. I would suspect a flaw in the non-maximum suppression logics. (In any case, there is an anisotropy somewhere.)
This said, edge detection on a binary image can be done by contour tracing.
I have the histogram for an image which i have calculated. I want to display this as an image so that I can actually see the histogram. I think my problem is to do with scaling although i am slightly confused over the co ordinate system starting with 0,0 in the top left as well.
int rows = channel.rows;
int cols = channel.cols;
int hist[256] = {0};
for(int i = 0; i<rows; i++)
{
for(int k = 0; k<cols; k++ )
{
int value = channel.at<cv::Vec3b>(i,k)[0];
hist[value] = hist[value] + 1;
}
}
Mat histPlot = cvCreateMat(256, 500,CV_8UC1);
for(int i = 0; i < 256; i++)
{
int mag = hist[i];
line(histPlot,Point(i,0),Point(i,mag),Scalar(255,0,0));
}
namedWindow("Hist",1);
imshow("Hist",histPlot);
This is my calculation for creating my histogram and displaying the result. If i do mag/100 in my second loop then i get some resemblance of a plot appearing (although upside down). I call this method whenever i adjust a value of my image, so the histogram should also change shape, which it doesn't appear to do. Any help in scaling the histogram and displaying it properly is appreciated.
please don't use cvCreateMat ( aka, the old c-api ), you also seem to have rows and cols wrong, additionally, if you want a color drawing, you need a color image as well, so make that:
Mat histPlot( 500, 256, CV_8UC3 );
image origin is top-left(0,0), so you've got to put y in reverse:
line(histPlot,Point(i,histPlot.rows-1),Point(i,histPlot.rows-1-mag/100),Scalar(255,0,0));
I'm trying to make a copy of the resizing algorithm of OpenCV with bilinear interpolation in C. What I want to achieve is that the resulting image is exactly the same (pixel value) to that produced by OpenCV. I am particularly interested in shrinking and not in the magnification, and I'm interested to use it on single channel Grayscale images. On the net I read that the bilinear interpolation algorithm is different between shrinkings and enlargements, but I did not find formulas for shrinking-implementations, so it is likely that the code I wrote is totally wrong. What I wrote comes from my knowledge of interpolation acquired in a university course in Computer Graphics and OpenGL. The result of the algorithm that I wrote are images visually identical to those produced by OpenCV but whose pixel values are not perfectly identical (in particular near edges). Can you show me the shrinking algorithm with bilinear interpolation and a possible implementation?
Note: The code attached is as a one-dimensional filter which must be applied first horizontally and then vertically (i.e. with transposed matrix).
Mat rescale(Mat src, float ratio){
float width = src.cols * ratio; //resized width
int i_width = cvRound(width);
float step = (float)src.cols / (float)i_width; //size of new pixels mapped over old image
float center = step / 2; //V1 - center position of new pixel
//float center = step / src.cols; //V2 - other possible center position of new pixel
//float center = 0.099f; //V3 - Lena 512x512 lower difference possible to OpenCV
Mat dst(src.rows, i_width, CV_8UC1);
//cycle through all rows
for(int j = 0; j < src.rows; j++){
//in each row compute new pixels
for(int i = 0; i < i_width; i++){
float pos = (i*step) + center; //position of (the center of) new pixel in old map coordinates
int pred = floor(pos); //predecessor pixel in the original image
int succ = ceil(pos); //successor pixel in the original image
float d_pred = pos - pred; //pred and succ distances from the center of new pixel
float d_succ = succ - pos;
int val_pred = src.at<uchar>(j, pred); //pred and succ values
int val_succ = src.at<uchar>(j, succ);
float val = (val_pred * d_succ) + (val_succ * d_pred); //inverting d_succ and d_pred, supposing "d_succ = 1 - d_pred"...
int i_val = cvRound(val);
if(i_val == 0) //if pos is a perfect int "x.0000", pred and succ are the same pixel
i_val = val_pred;
dst.at<uchar>(j, i) = i_val;
}
}
return dst;
}
Bilinear interpolation is not separable in the sense that you can resize vertically and the resize again vertically. See example here.
You can see OpenCV's resize code here.
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;
}
multiplying each pixel by the average blurring mask *(1/9) but the result is totally different.
PImage toAverageBlur(PImage a)
{
PImage aBlur = new PImage(a.width, a.height);
aBlur.loadPixels();
for(int i = 0; i < a.width; i++)
{
for(int j = 0; j < a.height; j++)
{
int pixelPosition = i*a.width + j;
int aPixel = ((a.pixels[pixelPosition] /9));
aBlur.pixels[pixelPosition] = color(aPixel);
}
}
aBlur.updatePixels();
return aBlur;
}
Currently, you are not applying an average filter, you are only scaling the image by a factor of 1/9, which would make it darker. Your terminology is good, you are trying to apply a 3x3 moving average (or neighbourhood average), also known as a boxcar filter.
For each pixel i,j, you need to take the sum of (i-1,j-1), (i-1,j), (i-1,j+1), (i,j-1), (i,j),(i,j+1),(i+1,j-1),(i+1,j),(i+1,j+1), then divide by 9 (for a 3x3 average). For this to work, you need to not consider the pixels on the image edge, which do not have 9 neighbours (so you start at pixel (1,1), for example). The output image will be a pixel smaller on each side. Alternatively, you can mirror values out to add an extra line to your input image which will make the output image the same size as the original.
There are more efficient ways of doing this, for example using FFT based convolution; these methods are faster because they don't require looping.