Error using Java OpenCV library with size - java

I am newly working with openCV in java on eclipse and am working on an eye tracking software, i am using a base code some one else has created and plan to tweek it but am having an error with a few lines of code and can not figure out why. here is the entire class
package have;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.highgui.VideoCapture;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
class FacePanel extends JPanel{
private static final long serialVersionUID = 1L;
private BufferedImage image;
// Create a constructor method
public FacePanel(){
super();
}
/*
* Converts/writes a Mat into a BufferedImage.
*
* #param matrix Mat of type CV_8UC3 or CV_8UC1
* #return BufferedImage of type TYPE_3BYTE_BGR or TYPE_BYTE_GRAY
*/
public boolean matToBufferedImage(Mat matrix) {
MatOfByte mb=new MatOfByte();
Highgui.imencode(".jpg", matrix, mb);
try {
this.image = ImageIO.read(new ByteArrayInputStream(mb.toArray()));
} catch (IOException e) {
e.printStackTrace();
return false; // Error
}
return true; // Successful
}
public void paintComponent(Graphics g){
super.paintComponent(g);
if (this.image==null) return;
g.drawImage(this.image,10,10,this.image.getWidth(),this.image.getHeight(), null);
}
}
class FaceDetector {
private CascadeClassifier face_cascade;
// Create a constructor method
public FaceDetector(){
// face_cascade=new CascadeClassifier("./cascades/lbpcascade_frontalface_alt.xml");
//..didn't have not much luck with the lbp
face_cascade=new CascadeClassifier("./cascades/haarcascade_frontalface_alt.xml");
if(face_cascade.empty())
{
System.out.println("--(!)Error loading A\n");
return;
}
else
{
System.out.println("Face classifier loooaaaaaded up");
}
}
public Mat detect(Mat inputframe){
Mat mRgba=new Mat();
Mat mGrey=new Mat();
MatOfRect faces = new MatOfRect();
inputframe.copyTo(mRgba);
inputframe.copyTo(mGrey);
Imgproc.cvtColor( mRgba, mGrey, Imgproc.COLOR_BGR2GRAY);
Imgproc.equalizeHist( mGrey, mGrey );
face_cascade.detectMultiScale(mGrey, faces);
System.out.println(String.format("Detected %s faces", faces.toArray().length));
for(Rect rect:faces.toArray())
{
Point center= new Point(rect.x + rect.width*0.5, rect.y + rect.height*0.5 );
//draw a blue eclipse around face
the error starts here with error code : "The constructor Size(double, double, int, int, int, Scalar) is undefined"
Size s = new Size( rect.width*0.5, rect.height*0.5), 0, 0, 360, new Scalar( 0, 0, 255 )
then here at ellipse i get an error of : "The method ellipse(Mat, RotatedRect, Scalar, int, int) in the type Core is not applicable for the arguments (Mat, Point, Size, int, int, int)
Core.ellipse( mRgba, center,s , 4, 8, 0 );
}
return mRgba;
}
}
public class Eyes {
public static void main(String arg[]) throws InterruptedException{
// Load the native library.
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
//or ... System.loadLibrary("opencv_java244");
//make the JFrame
JFrame frame = new JFrame("WebCam Capture - Face detection");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FaceDetector faceDetector=new FaceDetector();
FacePanel facePanel = new FacePanel();
frame.setSize(400,400); //give the frame some arbitrary size
frame.setBackground(Color.BLUE);
frame.add(facePanel,BorderLayout.CENTER);
frame.setVisible(true);
//Open and Read from the video stream
Mat webcam_image=new Mat();
VideoCapture webCam =new VideoCapture(0);
if( webCam.isOpened())
{
Thread.sleep(500); /// This one-time delay allows the Webcam to initialize itself
while( true )
{
webCam.read(webcam_image);
if( !webcam_image.empty() )
{
Thread.sleep(200); /// This delay eases the computational load .. with little performance leakage
frame.setSize(webcam_image.width()+40,webcam_image.height()+60);
//Apply the classifier to the captured image
webcam_image=faceDetector.detect(webcam_image);
//Display the image
facePanel.matToBufferedImage(webcam_image);
facePanel.repaint();
}
else
{
System.out.println(" --(!) No captured frame from webcam !");
break;
}
}
}
webCam.release(); //release the webcam
} //end main
}
any and all help would be greatly appreciated

If you have a look at the OpenCV Java API you can see the defined constructors for Size and what parameters are required:
http://docs.opencv.org/java/org/opencv/core/Size.html
A size holds two values, height and width.
So your code:
Size s = new Size( rect.width*0.5, rect.height*0.5), 0, 0, 360, new Scalar( 0, 0, 255 )
should be:
Size s = new Size( rect.width*0.5, rect.height*0.5);
this creates a size holding half the rectangle width and half the rectangle height.
And again for the Ellipse methods:
http://docs.opencv.org/java/org/opencv/core/Core.html
There are many variations but it looks like you want to be using the following:
public static void ellipse(Mat img,
Point center,
Size axes,
double angle,
double startAngle,
double endAngle,
Scalar color)
So your code:
Core.ellipse( mRgba, center,s , 4, 8, 0 );
is likely just missing the colour scalar:
Core.ellipse( mRgba, center,s , 4, 8, 0, new Scalar(B,G,R));
Where B,G,R are doubles for each colour channel.

Related

Java - Motion / Object detection (Human detection) using OpenCV

I got the video stream from cctv camera using opencv. Now I want to detect a simple motion / object from this video stream. For example, if any person come in any selected zone then rectangular border should be generated around him. I search some opencv tutorial, but I didn't got any proper solution or idea.I used the following code to display video stream.
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.highgui.Highgui;
import org.opencv.highgui.VideoCapture;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
public class VideoStream
{
static BufferedImage tmpImg=null;
public static void main(String args[]) throws InterruptedException
{
System.out.println("opencv start..");
// Load native library
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
VideoCapture camView=new VideoCapture();
camView.open("http://192.168.1.7:80/cgi-bin/view.cgi?chn=6&u=admin&p=");
if(!camView.isOpened())
{
System.out.println("Camera Error..");
}
else
{
System.out.println("Camera successfully opened");
}
videoCamera cam=new videoCamera(camView);
//Initialize swing components
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(cam);
frame.setSize(1024,768);
frame.setVisible(true);
while(camView.isOpened())
{
cam.repaint();
}
}
}
#SuppressWarnings("serial")
class videoCamera extends JPanel
{
VideoCapture camera;
Mat mat=new Mat();
public videoCamera(VideoCapture cam)
{
camera = cam;
}
public BufferedImage Mat2BufferedImage(Mat m)
{
int type = BufferedImage.TYPE_BYTE_GRAY;
if (m.channels() > 1)
{
type = BufferedImage.TYPE_3BYTE_BGR;
}
int bufferSize = m.channels() * m.cols() * m.rows();
byte[] b = new byte[bufferSize];
m.get(0, 0, b); // get all the pixels
BufferedImage img = new BufferedImage(m.cols(), m.rows(), type);
final byte[] targetPixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
System.arraycopy(b, 0, targetPixels, 0, b.length);
return img;
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Mat mat = new Mat();
camera.read(mat);
BufferedImage image = Mat2BufferedImage(mat);
g.drawImage(image,0,0,image.getWidth(),image.getHeight(), null);
}
}
Can any one knows how could we do that.
please check this code, but i implement it by python
import cv2, time, pandas
from datetime import datetime
first_frame = None
status_list = [None,None]
times = []
df=pandas.DataFrame(columns=["Start","End"])
video = cv2.VideoCapture('rtsp://admin:Paxton10#10.199.27.128:554')
while True:
check, frame = video.read()
status = 0
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray,(21,21),0)
if first_frame is None:
first_frame=gray
continue
delta_frame=cv2.absdiff(first_frame,gray)
thresh_frame=cv2.threshold(delta_frame, 30, 255, cv2.THRESH_BINARY)[1]
thresh_frame=cv2.dilate(thresh_frame, None, iterations=2)
(cnts,_)=cv2.findContours(thresh_frame.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in cnts:
if cv2.contourArea(contour) < 200000:
continue
status=1
(x, y, w, h)=cv2.boundingRect(contour)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0,255,0), 3)
status_list.append(status)
status_list=status_list[-2:]
if status_list[-1]==1 and status_list[-2]==0:
times.append(datetime.now())
if status_list[-1]==0 and status_list[-2]==1:
times.append(datetime.now())
#cv2.imshow("Gray Frame",gray)
#cv2.imshow("Delta Frame",delta_frame)
imS = cv2.resize(thresh_frame, (640, 480))
cv2.imshow("Threshold Frame",imS)
imS = cv2.resize(frame, (640, 480))
cv2.imshow("Color Frame",imS)
#cv2.imshow("Color Frame",frame)
key=cv2.waitKey(1)
if key == ord('q'):
if status == 1:
times.append(datetime.now())
break
print(status_list)
print(times)
for i in range(0, len(times), 2):
df = df.append({"Start": times[i],"End": times[i+1]}, ignore_index=True)
df.to_csv("Times.csv")
video.release()
cv2.destroyAllWindows

Fill in and detect contour rectangles in Java OpenCV

I have an initial starting image through some processing that looks like this
What I want to do it is to fill in the contours so it looks somewhat like this
and find the best fit parallelograms of the two (or more) squares which would let me get each one of the four bounding lines like this
If anyone could point me to the right functions that would help, but I can't find anything helpful. I've tried many distorted rectangle correctors but couldn't get them to work.
Heres current source code
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package javacvtesting;
import java.awt.FlowLayout;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
/**
*
* #author Arhowk
*/
public class JavaCVTesting {
/**
* #param args the command line arguments
*//*
*/
public static BufferedImage convert(Mat m){
Mat image_tmp = m;
MatOfByte matOfByte = new MatOfByte();
Highgui.imencode(".png", image_tmp, matOfByte);
byte[] byteArray = matOfByte.toArray();
BufferedImage bufImage = null;
try {
InputStream in = new ByteArrayInputStream(byteArray);
bufImage = ImageIO.read(in);
} catch (Exception e) {
e.printStackTrace();
}finally{
return bufImage;
}
}
public static Mat convert(BufferedImage i){
BufferedImage image = i;
byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
Mat mat = new Mat(image.getHeight(),image.getWidth(), CvType.CV_8UC3);
mat.put(0, 0, data);
return mat;
}
public static void show(BufferedImage i){
JFrame frame = new JFrame();
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(new JLabel(new ImageIcon(i)));
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat src = Highgui.imread("D:\\0_image.png");
Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2HSV);
Mat dest = new Mat();
// Mat dest = new Mat(src.width(), src.height(), src.type());
Core.inRange(src, new Scalar(58,125,0), new Scalar(256,256,256), dest);
Mat erode = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3,3));
Mat dilate = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5,5));
Imgproc.erode(dest, dest, erode);
Imgproc.erode(dest, dest, erode);
Imgproc.dilate(dest, dest, dilate);
Imgproc.dilate(dest, dest, dilate);
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(dest, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
Imgproc.drawContours(dest, contours, -1, new Scalar(255,255,0));
Panel p = new Panel();
p.setImage(convert(dest));
p.show();
}
}
To determine the outer contours you may use findContours with mode RETR_EXTERNAL:
List<MatOfPoint> contours = new ArrayList<>();
Mat dest = Mat.zeros(mat.size(), CvType.CV_8UC3);
Scalar white = new Scalar(255, 255, 255));
// Find contours
Imgproc.findContours(image, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// Draw contours in dest Mat
Imgproc.drawContours(dest, contours, -1, white);
Saving the obtained Mat you will get:
To fill in the obtained contours:
for (MatOfPoint contour: contours)
Imgproc.fillPoly(dest, Arrays.asList(contour), white);
Saving the new resulting Mat you will get this:
And to find the best fit rectangle for each contour:
Scalar green = new Scalar(81, 190, 0);
for (MatOfPoint contour: contours) {
RotatedRect rotatedRect = Imgproc.minAreaRect(new MatOfPoint2f(contour.toArray()));
drawRotatedRect(dest, rotatedRect, green, 4);
}
public static void drawRotatedRect(Mat image, RotatedRect rotatedRect, Scalar color, int thickness) {
Point[] vertices = new Point[4];
rotatedRect.points(vertices);
MatOfPoint points = new MatOfPoint(vertices);
Imgproc.drawContours(image, Arrays.asList(points), -1, color, thickness);
}
Finally you get this resulting image:
sounds like you want floodfill()

Difficulty with drawing in BufferedImage

I wrote a simple 2D game - Sokoban (http://www.game-sokoban.com/). I have a two-dimensional field on the screen. Separate JPanel Board is responsible for it.
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.io.IOException;
import java.util.Properties;
public class Board extends JPanel {
private static final String CONFIG_FILE_NAME = "ImageConfig.txt";
/** length px of a square cell */
private static final int SPACE = 20;
private Properties properties;
private Map<Status, Image> Images = null;
private Status[][] cells = null;
private BufferedImage canvas = null;
private Graphics2D g2d = null;
public Board() {
Properties properties = new Properties();
try {
//load a properties file
properties.load(ClassLoader.getSystemResourceAsStream(CONFIG_FILE_NAME));
}
catch (IOException ex) {
ex.printStackTrace();
}
Images = new HashMap<Status, Image>();
for (String key : properties.stringPropertyNames()) {
switch (key) {
case "AREA" : {
Images.put(Status.AREA, null);
break;
}
case "BAGGAGE" : {
Images.put(Status.BAGGAGE, null);
break;
}
case "FREE" : {
Images.put(Status.FREE, null);
break;
}
case "SOKO" : {
Images.put(Status.SOKO, null);
break;
}
case "WALL" : {
Images.put(Status.WALL, null);
break;
}
}
}
this.properties = properties;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(this.canvas, cells.length, cells[0].length, this);
}
public void setSize_(int height, int width) {
canvas = new BufferedImage(width * SPACE, height * SPACE, BufferedImage.TYPE_INT_RGB);
g2d = canvas.createGraphics();
setPreferredSize(new Dimension(width * SPACE, height * SPACE));
cells = new Status[height][width];
}
public void drawCell(int i, int j, Status status) {
cells[i][j] = status;
try {
g2d.drawImage(getImage(cells[i][j]), j * SPACE, i * SPACE, this);
}
catch (Exception e) {
e.printStackTrace();
}
repaint();
}
}
At each move the player on the field is updated, only two or three cells.
I want not to redraw all field, and to call only some calls of drawImage (...) on g2d and that changes were right there displayed on the screen. As to me to implement it (without paintComponent())?
You can utilize clipping to speed up painting process. You can set clip using setClip() method. Also, there is an overloaded version of repaint() that takes arguments to define only the region that requires updating. Swing sets the clip for you whenever you call repaint(x, y, width,height). Then inside paintComponent() you can choose to honor the clip. You can get it from Graphics with getClipBounds() method. And paint only the required area.
Take a look at Painting in AWT and Swing for more details and examples. Also see Performing Custom Painting tutorial, there is an example that illustrates clipped painting.
Use something like:
Rectangle rect = new Rectangle(i * SPACE, j * SPACE, SPACE, SPACE);
repaint(50L, rect);
The delay in ms, 50, is nice to the repainting process.
Not sure about i,j or j,i.

Image converter java

i'm working with image processing, and i have a question.
I want read an image from project, and convert the image to gray.
I'm currently trying to do conversion with the function rgb2gray, but still not working.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
public class ImageTesting extends Component {
private static int[] pixel;
private static BufferedImage b;
BufferedImage image;
public void paint(Graphics g) {
g.drawImage(image, 0, 0, null);
}
public ImageTesting() {
try {
image = ImageIO.read(new File("teste.jpg"));
} catch (IOException e) {
}
}
public Dimension getPreferredSize() {
if (image == null) {
return new Dimension(400, 400);
} else {
return new Dimension(image.getWidth(null), image.getHeight(null));
}
}
public static BufferedImage rgb2gray(BufferedImage bi) {
int heightLimit = bi.getHeight();
int widthLimit = bi.getTileWidth();
BufferedImage converted = new BufferedImage(widthLimit, heightLimit, BufferedImage.TYPE_BYTE_GRAY);
for (int height = 0; height < heightLimit; height++) {
for (int width = 0; width < widthLimit; width++) {
Color c = new Color(bi.getRGB(width, height) & 0x00fffff);
int newRed = (int) ((0.2989f * c.getRed()) * 2);// 0.2989f//multiplicr po 2
int newGreen = (int) ((0.5870f * c.getGreen()) * 2);// 0.5870f
int newBlue = (int) ((0.1140f * c.getBlue()) * 2);
int roOffset = newRed + newGreen + newBlue;
converted.setRGB(width, height, roOffset);
}
}
return converted;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException {
// TODO code application logic here
JFrame f = new JFrame("Load Image Sample");
JFrame g = new JFrame("Image RGB");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
g.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.add(new ImageTesting());
f.pack();
f.setVisible(true);
g.add(new ImageTesting());
rgb2gray(b);
}
}
When I run the program,these are the errors that appear.
If anyone could help me, i apreciate.
Thanks
Edit:
I managed to solve this problem,but now another question came up. To continue my work, i want to find the most 10 brilhants points in the resultant image, and return another image with black color in the index's that have the value 0, and white color in the index's that have value 1,but at this point i don't understand the best way to work out the steps.
It seems like there's something wrong with the main() method, isn't it? You create two completely identical JFrame instances, then add Imagetesting components that display the original image. And when running rgb2gray at the end, the result is sent nowhere.
I suggest using image filters, see related documentation here: http://www.jhlabs.com/ip/filters/
It's performant and simple to use.

Java - Window Image

Does anyone know how to capture a screen shot in Java (not it's own screen, but any other window on the desktop and they don't necessarily have to be the active window) in Windows? There are a number of threads here on this similar subject, but I have yet to find an answer.
I've tried using JNA, but became stuck after a few attempts. For example...
public class Main {
public static void main(String[] args) {
Main m = new Main();
List<WindowInfo> list = m.getWindows();
for (int i=0;i<list.size();i++)
{
WindowInfo info = list.get(i);
System.out.println(info.getTitle());
}
WindowInfo wi = list.get(0);
W32API.HDC hdcSrc = User32.instance.GetWindowDC(wi.getHwnd());
W32API.HDC hdcMemory = Gdi32.instance.CreateCompatibleDC(hdcSrc);
//W32API.HBITMAP hBitmapMemory = Gdi32.instance.CreateCompatibleBitmap(hdcSrc, int width, int height);
int width = wi.getRect().right - wi.getRect().left;
int height = wi.getRect().bottom - wi.getRect().top;
W32API.HBITMAP hBitmapMemory = Gdi32.instance.CreateCompatibleBitmap(hdcSrc, width, height);
W32API.HANDLE hOld = Gdi32.instance.SelectObject(hdcMemory, hBitmapMemory);
Gdi32.instance.BitBlt(hdcMemory, 0, 0, width, height, hdcSrc, width+2, height+2, 0x00CC0020);
/* # now how do we convert to a BufferedImage??? */
// clean up
Gdi32.instance.SelectObject(hdcMemory, hOld);
Gdi32.instance.DeleteDC(hdcMemory);
Gdi32.instance.DeleteObject(hBitmapMemory);
User32.instance.ReleaseDC(wi.getHwnd(), hdcSrc);
}
/**
*
* #return
*/
private List<WindowInfo> getWindows() {
final List<WindowInfo> list = new ArrayList<WindowInfo>();
User32.instance.EnumWindows(new WndEnumProc() {
public boolean callback(int hWnd, int lParam) {
if (User32.instance.IsWindowVisible(hWnd)) {
RECT r = new RECT();
User32.instance.GetWindowRect(hWnd, r);
byte[] buffer = new byte[1024];
User32.instance.GetWindowTextA(hWnd, buffer, buffer.length);
String title = Native.toString(buffer);
if (title!=null&&title.length()>0) {
list.add(new WindowInfo(hWnd, r, title));
}
}
return true;
}
}, 0);
Collections.sort(list, new Comparator<WindowInfo>() {
public int compare(WindowInfo o1, WindowInfo o2) {
int i1 = (o1.getTitle()!=null&&o1.getTitle().length()>0?o1.getTitle():" ").charAt(0);
int i2 = (o2.getTitle()!=null&&o2.getTitle().length()>0?o2.getTitle():" ").charAt(0);
return i1 - i2;
}
});
return list;
}
}
I've also tried the equivalent of "PrintWindow()" API...
Graphics g = form.CreateGraphics();
Bitmap bmp = new Bitmap(form.Size.Width, form.Size.Height, g);
Graphics memoryGraphics = Graphics.FromImage(bmp);
IntPtr dc = memoryGraphics.GetHdc();
bool success = PrintWindow(form.Handle, dc, 0);
memoryGraphics.ReleaseHdc(dc);
// bmp now contains the screenshot
Or do I have to use JNI, or any other tool?
Here's a working example.
The application being captured can't be minimized but it doesn't need to have focus or be on top (i.e. visible).
The code provided in the related C# thread, the MSDN article Capturing an Image and jmemoryeditorw provided the necessary pieces.
The code uses GetDC and GetClientRect to capture the client area of the window. They can be replaced by GetWindowDC and GetWindowRect if you want to capture the whole window including window decorations.
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import jna.extra.GDI32Extra;
import jna.extra.User32Extra;
import jna.extra.WinGDIExtra;
import com.sun.jna.Memory;
import com.sun.jna.platform.win32.GDI32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HBITMAP;
import com.sun.jna.platform.win32.WinDef.HDC;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinGDI;
import com.sun.jna.platform.win32.WinGDI.BITMAPINFO;
import com.sun.jna.platform.win32.WinNT.HANDLE;
public class Paint extends JFrame {
public BufferedImage capture(HWND hWnd) {
HDC hdcWindow = User32.INSTANCE.GetDC(hWnd);
HDC hdcMemDC = GDI32.INSTANCE.CreateCompatibleDC(hdcWindow);
RECT bounds = new RECT();
User32Extra.INSTANCE.GetClientRect(hWnd, bounds);
int width = bounds.right - bounds.left;
int height = bounds.bottom - bounds.top;
HBITMAP hBitmap = GDI32.INSTANCE.CreateCompatibleBitmap(hdcWindow, width, height);
HANDLE hOld = GDI32.INSTANCE.SelectObject(hdcMemDC, hBitmap);
GDI32Extra.INSTANCE.BitBlt(hdcMemDC, 0, 0, width, height, hdcWindow, 0, 0, WinGDIExtra.SRCCOPY);
GDI32.INSTANCE.SelectObject(hdcMemDC, hOld);
GDI32.INSTANCE.DeleteDC(hdcMemDC);
BITMAPINFO bmi = new BITMAPINFO();
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = WinGDI.BI_RGB;
Memory buffer = new Memory(width * height * 4);
GDI32.INSTANCE.GetDIBits(hdcWindow, hBitmap, 0, height, buffer, bmi, WinGDI.DIB_RGB_COLORS);
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
image.setRGB(0, 0, width, height, buffer.getIntArray(0, width * height), 0, width);
GDI32.INSTANCE.DeleteObject(hBitmap);
User32.INSTANCE.ReleaseDC(hWnd, hdcWindow);
return image;
}
public static void main(String[] args) {
new Paint();
}
BufferedImage image;
public Paint() {
HWND hWnd = User32.INSTANCE.FindWindow(null, "Untitled - Notepad");
this.image = capture(hWnd);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setExtendedState(MAXIMIZED_BOTH);
setVisible(true);
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.drawImage(image, 20, 40, null);
}
}
I had to define some extra functions that weren't included in platform.jar (which can be found on the JNA website).
package jna.extra;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.GDI32;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HDC;
import com.sun.jna.win32.W32APIOptions;
public interface GDI32Extra extends GDI32 {
GDI32Extra INSTANCE = (GDI32Extra) Native.loadLibrary("gdi32", GDI32Extra.class, W32APIOptions.DEFAULT_OPTIONS);
public boolean BitBlt(HDC hObject, int nXDest, int nYDest, int nWidth, int nHeight, HDC hObjectSource, int nXSrc, int nYSrc, DWORD dwRop);
}
package jna.extra;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HDC;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.win32.W32APIOptions;
public interface User32Extra extends User32 {
User32Extra INSTANCE = (User32Extra) Native.loadLibrary("user32", User32Extra.class, W32APIOptions.DEFAULT_OPTIONS);
public HDC GetWindowDC(HWND hWnd);
public boolean GetClientRect(HWND hWnd, RECT rect);
}
package jna.extra;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinGDI;
public interface WinGDIExtra extends WinGDI {
public DWORD SRCCOPY = new DWORD(0x00CC0020);
}
Use java.awt.Robot.createScreenCapture().
Here's an example:
try {
Robot robot = new Robot();
Rectangle size = new Rectangle(Toolkit.getDefaultToolkit()
.getScreenSize());
BufferedImage buf = robot.createScreenCapture(size);
ImageIO.write(buf, "png", new File("d:/test.png"));
} catch (AWTException ae) {
throw new RuntimeException("something went wrong");
}
Code originally stolen from here.
For your original question, here it goes.
Capturing an inactive window in Windows is pretty straightforward, using the robot class, ONLY and ONLY if the window is visible at the moment of capturing. If you want to avoid that requirement, you HAVE to use the DWM API.
Using the normal Windows API (pre Vista), you can use GetWindowRect(handle,RECT) where handle is a handler to the window you want to capture. This will get you a RECT object (I assume you are using JNA), here is the sequence of code you should write:
RECT dimensionsOfWindow = new RECT();
GetWindowRect( handlerToWindow, dimensionsOfWindow );//now in the dimensionsOfWindow you have the dimensions
Robot robot = new Robot();
BufferedImage img = robot.createScreenCapture( dimensionsOfWindow.toRectangle() );//now in the img object you have only the image of your desired window
However!! This will work as a charm ONLY if your window is currently visible. If it is minimized, you will get some exception in java (because it has negative x and y ). And if it is partially hidden, you will also screenshot the other windows that are on top of it.
You can't solve your problem on boxes that don't have dwm (Desktop Windows Manager) as it has an API that allows different windows to write to a temp buffer before they actually are painted to the screen.
On XP and non - running DWM machines, however, you are stuck with the code I gave you.
Additionally , you can take a look at the following question:
link text
Edit:
Here is an interesting guide (in C#, though, but you can use JNA+Java applying the same principles) that will give you a better understanding of the DWM and how to use it to do EXACTLY what you want.
link text
EditEdit
Just saw you have a link to the same guide in C# that I gave you. What seems to be the problem in just rewriting the code for Java/JNA?
EditEditEdit
To answer your additional question (how to convert your BitBit to a BufferedImage ), here is a guy who did it in his Open Source project. It is a nice piece of work and give him some appreciation:
http://code.google.com/p/jmemoryeditorw/
You might notice that if you run the program, it will give you all the processes and also...their Icons. If you dig in the code, you will see how they are converted from BitBit to BufferedImages.
Cheers and I have to say, a very nice question.

Categories

Resources