I'm working on a java image processing program(based on OpenCV library).
I need to remove the noise from the first image to get a clean image like the second image below.
In this specific case, what are the best ways to remove noise?
The most important part is how to remove the black parts that surround the image.
First image:
Second image:
get rid of gray: threshold the image so that any gray becomes white
get rid of border: floodfill at location (0.0) with white
That will leave just a few remaining issues to clean up: detect each black blob in the image, if the area of a blob is less than some amount floodfill the blob with white. One way to implement that follows.
Note that floodfill returns the number of pixels it filled. This allows you to scan for a black pixel, when you find one fill with gray. If the filled are is too small, fill again with white to erase the blob and then keep scanning, otherwise leave the blob as gray and keep scanning for black. At the end everything you want will be gray, so scan the image again and whenever you find gray flood fill with black.
Following program may be helpful to solve your problem,
This program thresholds the input image and selects only connected
components with particular size
#include <iostream>
#include<cv.h>
#include<highgui.h>
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
IplImage *img1 = cvLoadImage(argv[1] , 0);
IplImage *img3 = cvLoadImage(argv[1]);
IplImage *img2 = cvCloneImage(img1);
cvNamedWindow("Orig");
cvShowImage("Orig",img1);
cvWaitKey(0);
cvAdaptiveThreshold(img1, img1, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C,
CV_THRESH_BINARY_INV, 15);
cvNamedWindow("Thre");
cvShowImage("Thre",img1);
cvWaitKey(0);
IplImage *tempImg = cvCloneImage(img1);
CvMemStorage *storage = cvCreateMemStorage(0);
CvSeq *contour = NULL;
cvFindContours(tempImg, storage, &contour, sizeof(CvContour),
CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
for (; contour != 0; contour = contour->h_next)
{
CvRect r = cvBoundingRect(contour);
int area = r.width * r.height;
if (area < 50 || area > 500) continue;
cvRectangle(img3, cvPoint(r.x, r.y), cvPoint(r.x + r.width, r.y + r.height),
CV_RGB(255, 0, 0), 1);
}
cvNamedWindow("D");
cvShowImage("D",img3);
cvWaitKey(0);
}
Related
i am a newcomer in OpenCV and i am creating an OMR(Optical Mark Recognition) system in Java in order to detect the answers on a multiple choice paper sheet.I have created a form that consists of one big rectangle that is used for answering the questions by drawing the right circle, and one smaller rectangle that is for detecting a unique number that is the identity of the one answering.
Here is the image of the form:
Now my programm is detecting the upper rectangle the AM one but cannot detect the bigger one. My image is passing though 6 stages 1st dilation,2nd gray, 3rd threshold,4th blur,5th canny and 6th adaptiveThreshold. Here you can see that
dilated1 = new Mat(source1.size(), CV_8UC1);
dilate(source1, dilated1, getStructuringElement(MORPH_RECT, new Size(3, 3)));
gray1 = new Mat(dilated1.size(), CV_8UC1);
cvtColor(dilated1, gray1, COLOR_BGR2GRAY);
thresh1 = new Mat(gray1.rows(), gray1.cols(), gray1.type());
threshold(gray1, thresh1, 0, 255, THRESH_BINARY + THRESH_OTSU );
blur1 = new Mat(thresh1.size(), CV_8UC1);
blur(gray1, blur1, new Size(5.,5.));
canny1 = new Mat(blur1.size(), CV_8UC1);
Canny(blur1, canny1,160, 80);
adaptiveThresh1 = new Mat(canny1.rows(), gray1.cols(), gray1.type());
adaptiveThreshold(canny1, adaptiveThresh1, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 11,2);
I am also using findContours like that
findContours(adaptiveThresh1.clone(), contours1, hierarchy1, RETR_TREE, CHAIN_APPROX_SIMPLE);
I have created two different java classes because there are other things to detect in the small rectangle and other on the bigger. The code above is what i am using in order to try detecting the bigger rectangle. I have tried many different numbers in all the steps and still nothing.
When i am using only the bigger rectangle in an image it works just fine, but in combination with the other one it cannot detect it. It's for my thesis and its really important for me. Any help is appreciated and whatever you want me to add in order to help you please let me know.
You can find the largest contour and second largest contour.
Suggested stages:
Convert image to Grayscale (as you did).
Draw thick white rectangle around the image - making sure there is not black contour around the image.
Apply threshold and convert to binary (as you did).
The code I posted also inverse polarity, for the contours to be white.
Find contours.
Use RETR_EXTERNAL instead of RETR_TREE, because you don't need to find contours within contours.
Iterate the contours, and find the one with the largest area, and the one with the second largest area.
The contours with the largest area is the lower rectangle.
The contours with the second largest area is the upper rectangle.
Here is a Python implementation (not JAVA, but close enough):
import cv2
# Read input image
img = cv2.imread('image.png')
# Draw thick rectangle around the image - making sure there is not black contour around the image
cv2.rectangle(img, (0, 0), (img.shape[1], img.shape[0]), (255, 255, 255), thickness = 5)
# Convert from BGR to Grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply threshold on gray image - use automatic threshold algorithm (use THRESH_OTSU) and invert polarity.
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# Find contours
cnts, heir = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
max_a = 0 # Maximum area
smax_a = 0 # Second maximum area
max_c = [] # Contour with maximum area
smax_c = [] # Contour with second maximum area (maximum excluding max_c)
# Iterate contours
for c in cnts:
area = cv2.contourArea(c)
if area > max_a: # If area is grater than maximum, second max = max, and max = area
smax_a = max_a
smax_c = max_c # Second max contour gets maximum contour
max_a = area
max_c = c # Maximum contour gets c
elif area > smax_a: # If area is grater than second maximum, replace second maximum
smax_a = area
smax_c = c
#Get bounding rectangle of contour with maximum area, and mark it with green rectangle
x, y, w, h = cv2.boundingRect(max_c)
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), thickness = 2)
#Get bounding rectangle of contour with second maximum area, and mark it with blue rectangle
x, y, w, h = cv2.boundingRect(smax_c)
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), thickness = 2)
# Show result (for testing).
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
can anyone help me and tell me how to create a gray scale image where one pixel of the image is shown as a square with the size 2 x 2?
I already searched for help and found this how to create a gray scale image from pixel values using java but i don't know how to create a gray scale with the information that one pixel is shown as a square with the size 2 x 2.
thanks!
to create a picture where each pixel has the size 2x2 you must either scale the image (factor 2) for display only... or if you want to create a image you have to do it manually and create an image and draw with scale factor 2 on it
int[] pixels = ... //we already have our gray scale pixels here
int widthOriginal = ... //size of original image
int heightOriginal = ...
//let's create an buffered Image twice the size
BufferedImage img =
new BufferedImage(2*widthOriginal, 2*heightOriginal, BufferedImage.TYPE_4BYTE_ABGR);
//we paint on the buffered image's graphic...
Graphics gr = img.getGraphics();
//we draw all pixels on the graphic
for(int y = 0; y < heightOriginal; y ++){
for(int x = 0; x < widthOriginal; x ++){
int index = y*widthOriginal + x;
int gray = pixels[index];
//to draw we first set the color
gr.setColor(new Color(gray));
//then draw the pixel
gr.drawRect(2*x, 2*y,2,2); //draw a 2x2 pixel instead of a 1x1 pixel
}
}
uhm - honestly i've written that code entirely out of my head, so there may be some minor compilation problems... but the technique is explained properly...
I am developing a project on image processing where I have to fill the digitized images of cracked paintings. I have to convert a color image to grayscale, performing some calculations on the 2D Array of the gray image and writing it back as gray image. The code for this is:
BufferedImage colorImage=ImageIO.read(new File(strImagePath));
BufferedImage image = new BufferedImage(colorImage.getWidth(),colorImage.getHeight(),BufferedImage.TYPE_BYTE_GRAY);
Graphics g = image.getGraphics();
g.drawImage(colorImage, 0, 0, null);
g.dispose();
ImageIO.write(image,"PNG",new File("Image.PNG"));
BufferedImage imgOriginal=ImageIO.read(new File("Image.PNG"));
int width=image.getWidth();
int height=image.getHeight();
BufferedImage im=new BufferedImage(width,height,BufferedImage.TYPE_BYTE_GRAY);
int arrOriginal[][]=new int[height][width];
for(int i=0;i<height;i++)
for(int j=0;j<width;j++)
arrOriginal[i][j]=imgOriginal.getRGB(j,i)& 0xFF;
for(int i=0;i<height;i++)
for(int j=0;j<width;j++)
im.setRGB(j,i,arrOriginal[i][j]);
ImageIO.write(im,"PNG",new File("Image1.PNG"));
But the output image is very much darker, I am not getting the original image back (I have not done any changes yet).
I think there should be some changes in setRGB() statement but I don't know what.
To write image back, I have also tried:
`
BufferedImage im = new BufferedImage(width,height,BufferedImage.TYPE_BYTE_GRAY);
WritableRaster raster = im.getRaster();
for(int i=0;i<height;i++)
for(int j=0;j<width;j++)
raster.setSample(j,i,0,arrOriginal[i][j]);
`
But it also don't give me original image back.
Can anyone provide me the solution of this problem?
Thanks in advance.
I don't know anything about Java-based image processing, but I do know quite a lot about image processing in general, so I will see if I can give you any ideas. Please don't shoot me if I am wrong - I am just suggesting an idea.
In a greyscale image, the red, green and blue values are all the same, i.e. Red=Green=Blue. So, when you call getRGB and do the AND with 0xff, you are probably getting the blue component only, but that is ok as the red and green are the same - because it's greyscale.
I suspect the problem is that when you write it back to create your new output image, you are only setting the blue component and not the red and green - which should still be the same. Try writing back
original pixel + (original pixel << 8 ) + (original pixel <<16)
so that you set not only the Blue, but also the Red and Green components.
I've looked at JavaCV wrapper for OpenCV library and I saw that it is possible to use that library in Java for face detection on an image, but I was wondering is it possible to use that library for detecting traffic warning signs on an image and how?
I have pictures taken from the road that look like this: http://www.4shared.com/photo/5QxoVDwd/041.html
and the result of detection should look sometning like this or similar: http://www.4shared.com/photo/z_pL0lSK/overlay-0.html
EDIT:
After I detect red color I get this image:
And I have a problem detecting just the warning sign triangle shape and ignore all other shapes. I tried changing the cvApproxPoly parameters but with no result. This is my code:
public void myFindContour(IplImage image)
{
IplImage grayImage = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1);
cvCvtColor(image, grayImage, CV_BGR2GRAY);
CvMemStorage mem;
CvSeq contours = new CvSeq();
CvSeq ptr = new CvSeq();
cvThreshold(grayImage, grayImage, 150, 255, CV_THRESH_BINARY);
mem = cvCreateMemStorage(0);
cvFindContours(grayImage, mem, contours, Loader.sizeof(CvContour.class) , CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
Random rand = new Random();
while (contours != null && !contours.isNull()) {
if (contours.elem_size() > 0) {
CvSeq points = cvApproxPoly(contours, Loader.sizeof(CvContour.class),
mem, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);
Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
CvScalar color = CV_RGB( randomColor.getRed(), randomColor.getGreen(), randomColor.getBlue());
cvDrawContours(image, points, color, CV_RGB(0,0,0), -1, CV_FILLED, 8, cvPoint(0,0));
}
contours = contours.h_next();
}
cvSaveImage("myfindcontour.png", image);
}
This is the output that i get (I used different colors for every shape, but in the final output i will use only white for detected warning sign and everything other left black):
You have to do the following:
Detect red color on image - you will get 1bit image where: 0=non-red, 1=red.
Detect triangles on created in previous step image. You can do that using approxPoly function.
see ,the first find the contour area.
compare it with the precalculated value and keep it with in a range
like
if(area>Mincontourarea && area<maxcontourare)
{
thats it now we have the signboard do
}
the value if calculated wouldnot be bigger than the car conotur,
to get the contoutr
up to my knowledge u need
Moments operator
code for the momnts operator:
Moments moment = moments((cv::Mat)contours[index]);
area = moment.m00; //m00 gives the area of the detected contour
put the above code before the if block discussed above
if you want the x and y coordinates put a post again..
take a look of my answer, it is in c++ but using opencv it is able to detect road signs, you can take it as a good example.
https://stackoverflow.com/a/52651521/8035924
I'm saving a very large PNG (25 MB or so) with Java. The problem is that while it's being generated, it's using 3+ gigabytes of memory, which is not ideal since it severely slows down systems with low memory.
The code I'm working with needs to combine a set of tiled images into a single image; in other words, I have nine images (PNG):
A1 A2 A3
B1 B2 B3
C1 C2 C3
which need to be combined into a single image.
The code I'm using is this:
image = new BufferedImage(width, height, height, BufferedImage.TYPE_INT_ARGB_PRE);
g2d = image.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
// draw the 9 images on here at their proper positions...
// save image
g2d.dispose();
File file = getOutputFile();
ImageIO.write(image, "png", file);
Is there a way to make and save an image without having the entire image in memory?
Edit:
To draw the images, I'm doing this in a loop:
BufferedImage tile = ImageIO.read(new File("file.png"));
g2d.drawImage(tile, x, y, w, h);
This is being repeated many times (it's usually about 25x25, but sometimes more), so if there is even a small memory leak here, that could be causing the problem.
You can also take a look at this PNGJ library (disclaimer: I coded it), it allows to save a PNG image line by line.
ImageIO.write(image, "png", file); is internally using com.sun.imageio.plugins.png.PNGImageWriter. That method and that writer expect image to be a rendered image but PNG writting is done by 'bands' so you can make a subclass of RenderedImage that generates the requested bands of the composed large image as the writer ask for that bands to the image.
From PNGImageWriter class:
private void encodePass(ImageOutputStream os,
RenderedImage image,
int xOffset, int yOffset,
int xSkip, int ySkip) throws IOException {
// (...)
for (int row = minY + yOffset; row < minY + height; row += ySkip) {
Rectangle rect = new Rectangle(minX, row, width, 1); // <--- *1
Raster ras = image.getData(rect); // <--- *2
*2 I think this is the only place where the writer reads pixels from you image. You should make a getData(rect) method that computes that rect joining 3 bands from 3 images into one.
*1 As you see it reads bands with a height of 1 pixel.
If the things are as I think you should only need to compose 3 images at a time. There would be no need for the other 6 to be in memory.
I know it is not an easy solution but it might help you if you don't find anything easier.
Would using an external tool be an option? I remember using ImageMagick for similar purpose, you would need to save your smaller images first.