Bildqualität verringern

M

Mr.Bross

Gast
Hi,

Ich schreibe an einem Programm, das ähnlich wie TeamViewer funktionieren soll(Also Desktop übertragung usw.). Momentan erstelle ich immer Screenshots mit Robot.createScreenCapture(Rectangle) und verschicke diese dann zu dem Server. Jedoch dauert dies sehr lang und braucht pro Bild etwa 4 Sekunden um es su verschicken.
Gibt es in Java eine Möglichkeit, das Bild zu verkleinern (Also z.B. nur jeden zweiten Pixel verschicken o.ä.), dass es dann schneller wird?? Oder kann man dieses Problem auch anders lösen??
 

Marco13

Top Contributor
Komprimierst du das ganze schon, als PNG zum Beispiel?

Man kann einen ByteArrayOutputStream an ImageIO.write übergeben, und dort dann den byte array mit den PNG-Daten rausholen...
 
M

Mr.Bross

Gast
Was bringt mir das, wenn ich es als PNG abspeichere?? Es wird ja dadurch nicht schneller oder?? Dann müsste ich es abspeichern, laden und verschicken, sind mehr Schritte als vorher. Oder wird die Größe dadurch stark verkleinert und insgesammt ist es dann doch schneller????
 

Wildcard

Top Contributor
Netzwerkübertragung ist immer wesentlich teuerer als Speicheroperationen. PNG Bilder sind um viele Faktoren kleiner als unkromprimierte Bitmaps, ein Bildkompressionsverfahren wie eben zB PNG reduziert den Traffic also schon deutlich.
Übrigens, nicht als PNG (physikalisch) abspeichern, sondern nur In-Memory und die komprimierten Bits dann verschicken.
 
M

Mr.Bross

Gast
Tut mir leid, aber kannst du bitte ein Beispiel zeigen, wie du das meinst mit physikalisch abspeichern?? Ich hab davon leider noch nichts gehört.
 

ice-breaker

Top Contributor
auch ein PNG wird dich nicht großartig weiterbringen, du müsstest nur den Bereich senden, der sich seit dem letzten Screenshot auch wirklich verändert.
TeamViewer versendet es dann zeilenweise (also jede 4. Zeile, dann jede 3 ...) und erreicht damit dass das Bild scheinbar schneller aufgebaut wird.
 
M

Mr.Bross

Gast
Also ich habe jetzt mal mit getRGB(x,y) jedes einzelne Pixel beider Bilder(das jetztige und das vorherige) ausgelesen und in einem zweidimensionalen int-Array gespeichert. Dann habe ich es durch

Java:
if (pixel1[x][y] != pixel2[x][y])
				{
					image2.setRGB(x, y, 0);
				}

ausgelesen. Doch nun möchte ich ja nur den Teil verschicken, der sich verändert hat. Wie kann man das jetzt machen? Wenn ich es jetzt so verschicke, habe ich trotzdem noch das ganze Bild, nur dass überall eine "0" steht, wo sich etwas verändert hat (Also Farbwert 0).

PS: Wenns geht auch ein kleines Beispiel dazu schreiben :)
 
M

Mr.Bross

Gast
Tut mir leid nicht ausgelesen, sondern verglichen. Hier nochmal das ganze:

Java:
for (int x = 0; x < breite; x++)
		{
			for (int y = 0; y < hoehe; y++)
			{
				pixel1[x][y] = image1.getRGB(x, y);
				pixel2[x][y] = image2.getRGB(x, y);

				if (pixel1[x][y] != pixel2[x][y])
				{
					image2.setRGB(x, y, 0);
				}

			}

		}
 

Marco13

Top Contributor
Je nach Bildtyp und Bildgröße kann das getRGB so lange dauern, dass es sich nicht lohnt. Im schlimmsten Fall stellt man ohnehin fest, dass sich ALLES geändert hat, und man auch ALLES übertragen muss.

Man könnte auch sagen, dass die Entscheidung über die Frage, WAS übertragen werden muss, unabhängig von der Frage ist, WIE die Daten dann übertragen werden.

Die Übertragung des Bildes könnte man mit ImageIO (Java Platform SE 6) etwa so machen:
Java:
// Senden
BufferedImage image = ...
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
byte data[] = baos.toByteArray();

verschicke(data, networkOutputStream);

// Empfangen:
BufferedImage image = ImageIO.read(networkInputStream);


Die Bestimung der Bereiche, die sich geändert haben (könnte man als "Dirty Rectangle Detection" bezeichnen) kann beliebig kompliziert sein. Ein einfacher Pixelvergleich ist zwar einfach, aber ziemlich langsam. Ob und wie man für sowas Techniken wie KD-Trees oder irgendwas mit Hashing einsetzen kann, muss man sich noch überlegen. Mit ziemlicher Sicherheit wird das aber nur "schnell", wenn man ein bißchen in den Innereien eines BufferedImages rumwühlt (d.h. sicherstellen, dass es vom TYPE_INT_RGB ist, und sich den DataArray als int[] array holt oder so).
 
M

Mr.Bross

Gast
Hi,

ich habe es jetzt einfach mal das ganze int-Array zu verschicken(Da ich nicht wusste wie das andere geht). Als ich es innerhalbt des Routers (2 Rechner unter einem Router) brauchte es pro Bild etwa 300 millisekunden, bis es an kam.
Jetzt habe ich es jedoch zwar innerhalb eine Routers probiert, aber über die externe IP-Adresse.
Jetzt gab es schon sofort das erste Problem: Alleine zum Verbinden mit dem Server, hat es 2 Minuten gedauert. Und dann sind die Bilder nicht gleichmäßig angekommen, sondern einmal 20, dann 30 Sekunden nichts, dann 30 usw. .

Kann mir jemand erklären, warum diese nur so gebündelt ankommen und zwischendrin gar nichts ist?? Ich benutze doch flush() da dürfte dies eigentlich nicht passieren... Und wieso dauert es fast 2 minuten zum verbinden?? Ich habe eine recht schnelle Leitung ( 34000 MB / s)
 
M

Mr.Bross

Gast
Hi,

Ich hab noch ein Problem gefunden :) : Wenn ich mit createScreenCapture(rect) mehrer Screenshot mache, sind diese nicht aktuell.
Dieser wird nur alle 2 Sekunden oder so aktualisiert. Kann man diesen vielleicht irgendwie manuell aktualisieren??
 

Marco13

Top Contributor
Klingt insgesamt nach mehreren Baustellen... vielleicht solltest du die Probleme der Reihe nach lösen. Erstmal sicherstellen, dass die Netwerksache funktioniert. Warum dein Router manchmal 2 Minuten braucht, weiß hier niemand, und das hat vermutlich auch nichts mit Java zu tun. Dann das mit den Screenshots. Erklären, wie die bisher gemacht werden, und wie sie weiterverarbeitet werden. Dann überlegen, wie man die Sachen überträgt....
 
M

Mr.Bross

Gast
Wenn es wirklich am Router liegt, kann ich da wohl nichts ändern. Aber wie ich das mit den Screenshots hinkriege, weiß ich nicht. Ich weiß leider nicht wie ich es sonst machen soll.
 

Marco13

Top Contributor
Beschreib' mal wie du die Screenshots im Moment machst. Läuft da irgendein Thread, der dumpf einfach so viele Screenshots macht, wie möglich, und die ins Netzwerk stopft? Oder hast du dir da einen Mechanismus überlegt, der dafür sorgt, dass ein neuer Screenshot erst dann versendet wird, wenn der alte verschickt wurde?

Ich hatte mal ein ähnliches Programm geschrieben, und da lief es auf sowas raus wie
Code:
                                   1s. Server wartet auf Bildanfrage
1c. Client sendet Bildanfrage
2c. Client wartet auf Bild
                                   2s. Server empfängt Bildanfrage
                                   3s. Server Schickt Bild raus
                                   4s. Server geht zu 1s
3c. Client empfängt Bild
4c. Client verarbeitet Bild
5c. Client geht zu 1c

Um sicherzustellen, dass Bilder nur so schnell rausgeschickt werden, wie sie auch empfangen und verarbeitet werden können. Das ganze auch für mehrere Clients und unterschiedliche Bildtypen ... also in Wirklichkeit etwas komplizierter, als es hier aussieht :D
 
M

Mr.Bross

Gast
Ich mache meine Screenshots so:

Java:
	while (state == 1)
                 {
						if (x == 20)
						{

							x = 0;

							ooutputs.reset();

							image = null;

							try
							{
								System.gc();
								Thread.sleep(100);
								Thread.yield();

							}
							catch (InterruptedException e)
							{
								e.printStackTrace();
							}

						}

						try
						{
							Thread.sleep(200);
						}
						catch (InterruptedException e)
						{
							e.printStackTrace();
						}

						image = robot.createScreenCapture(rect);

						for (int xx = 0; xx < breite1; xx++)
						{
							for (int yy = 0; yy < hoehe1; yy++)
							{
								pixel[xx][yy] = image.getRGB(xx, yy);

							}

						}

						ooutputs.writeObject(pixel);

						ooutputs.flush();

						Thread.yield();

						x++;

		          }

(rect = Rectangle)

Ich habe auch schon probiert, immer einen neuen Robot zu machen, geht leider auch nicht.

Schritte:

1c. Client wartet auf Bildanfrage
1s. Server sendet Bildanfrage
2s. Server wartet auf Bilder
2c. Client empfängt Bildanfrage
3c. (Code) :


1. Thread schläft 200 millisekunden
2. Robot erzeugt Screenshot und speichert ihn im BufferedImage image
3. BufferedImage wird in Pixel aufgeteilt und in dem zweidimensionalen int-Array gespeichert.
4. ObjectOutputStream verschickt das Bild(Eigentlich das int-Array)


3s. Server bekommt Bild und zeigt es an

4c. Code wird so lange ausgeführt (und Server empfängt so lange) , bis der Server schreibt, dass der Client aufhören soll.
 

Marco13

Top Contributor
Ja, was der Code mit dem x==20 und dem System.gc() und so dort soll erschließt sich mir nicht so ganz, aber... dort erkennt man jetzt keine Vorkehrungen, die verhindern, dass z.B. pro Sekunde 5 Bilder gemacht und verschickt werden sollen, obwohl z.B. nur 2 Bilder pro Sekunde verschickt und auf der anderen Seite verarbeitet werden können...
 

ice-breaker

Top Contributor
Sollte x=20 sein sind das ja für den Snippet jeweils schon mind. 300ms Wartezeit.
Das bedeutet in eine Sekunde würden 3 solche Wartezeiten reinpassen, wenn wir davon ausgehen, dass das Bilder erzeugen und verschicken für jedes Bild länger als 33msec dauert, dann komme ich ziemlich genau auf 2 Bilder pro Sekunde und per Maximalwert sind überhaupt nur 3 Bilder pro Sekunde möglich.

Du solltest die Sleep-Befehle komplett loswerden, du möchtest eine möglichst Echtzeitübertragung der Bilder und bindest dann Wartezeiten ein? Das passt nicht zusammen.
Die yield-Aufrufe sollten da auch raus.
 
M

Mr.Bross

Gast
Die sleep(), yield() und gc() -Befehle waren eigentlich dazu da, dass die Bilder aktuell sind(Wenn ich mehrere Bilder schneller verschicke (bei langsamer Leitung), dann werden die Bilder immer mehr verzögert) und dass es nicht so auf den Arbeitsspeicher drückt :) . Ich habe sie jetzt trotzdem weg gemacht, was leider mein Problem nicht ändert. Die Bilder sind leider nicht aktuell. Es werden etwa 6 Bilder pro Sekunde verschickt (Innerhalb des Routers). Doch wenn ich beim Clienten z.B. ein Programm starte und man es dort schon sieht, dauert es noch etwa 5 Sekunden, bis man es beim Server sieht. (Obwohl die Bilder fast gleich losgeschickt werden und ankommen)
 

Marco13

Top Contributor
Ich bin kein Netzwerkexperte, aber ... wenn der Client Bilder schneller rausschicken will, als das Netzwerk bwz. der Server verarbeiten kann, dann liegen liegen diese Bilder ja noch irgendwo in einem Puffer. D.h. wenn man dann ein neues, geändertes Bild (mit der gestarteten Applikation) rausschickt, dann KÖNNTE es sein, dass erstmal noch ein paar Sekunden lang "alte" Bilder beim Server ankommen. Aus genau diesem Grund hatte ich damals dieses strenge, "Handshake-artige" Verfahren eingebaut, wo der Empfänger jedes Bild explizit anfordert (und das NUR wenn er das letzte Bild schon fertig verarbeitet hat) und der Sender NUR Bilder rausschickt, wenn vorher eine Anfrage kam.
Etwas ... drastisch formuliert: Wenn in deinem Code kein "import java.util.concurrent.*" steht, ist das (für mich) schon dubios. Aber vielleicht gibt es auch eine gaaanz einfache Alternative, auf die ich wegen mangelnder Netzwerkkenntnisse damals nicht gekommen bin ;)
 
M

Mr.Bross

Gast
Erstmal danke, ich werde mal dein Verfahren ausprobieren, ob das besser klappt. Aber was du mit "import java.util.concurrent.*" meinst, weiß ich nicht. Das seh ich zum ersten mal...
 

Marco13

Top Contributor
Das ist ein package mit einiges Hilfsklassen, u.a. für synchronisation. Das KANN man verwenden, um eben genau das nachzubauen: Ein Thread beim Sender wartet so lange, bis ein "Image Request" gelesen wurde. Dann wird ein Bild rausgeschickt, und wieder in den Wartezustand gegangen. Auf der anderen Seite wurde der Image Request rausgeschickt, und dann auf das Bild gewartet. Sobald das Bild empfangen und verarbeitet ist, wird ein neuer Image Request gesendet.
 
M

Mr.Bross

Gast
Hi,

Ich habe ein Testprogramm gechrieben, das immer eine Anfrage stellt und daraufhin der Screenshot geschickt wird. Doch leider sind die Bilder trotzdem nicht aktuell :-( Das sehe ich schon alleine durch die Ausgabe(Wieveiel Bilder es sind) beim Clienten. Die Ausgabe ist ja auch beim Server auf dem Screenshot zu erkennen, so kann man es vergleichen.
Die Bilder kommen wieder fast sofort an (vllt 100ms), jedoch bleibt die Ausgabe immer ein paar Sekunden stehen.
Wenn man beim Clienten auf die Ausgabe schaut, läuft es durchgehend weiter. Doch beim Server (Screenshot) bleibt es immer bei z.B. 20 stehen bis komischerweise output.reset() aufgerufen wird. Dann wird das Bild erst aktualisiert. Habe es getestet, indem ich alle 2 Bilder aktualisiert habe.Es kann aber auch daran liegen, dass es immer 4-5 Sekunden dauert, bis reset() fertig ist.
Wenn ich es jedoch mit "alle 20 mal" reset() mache, ist das Bild, das aktualisiert ist (also das wo anders aussieht), trotzdem nicht aktuell. Es liegt immer zwischen 15 und 20 Bildern zurück. Aber wie bereits gesagt kommen die Bilder fast synchron an.

Hier mal der Quellcode von meinem Testprogramm: (Vielleicht wills ja mal jemand testen, obs bei ihm geht)

Server:

Java:
package de.kuehne.writeimage;

import java.awt.AWTException;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Server {

	private int x;
	private int y;

	public static void main(String args[]) throws IOException,
			InterruptedException
	{

		new Server().los();

	}

	public void los() throws IOException, InterruptedException
	{

		ServerSocket sock = new ServerSocket(5005);

		Socket socket = sock.accept();

		ObjectInputStream in = new ObjectInputStream(socket.getInputStream());

		PrintWriter writer = new PrintWriter(socket.getOutputStream());

		int width = 1024;
		int height = 768;

		JFrame frame = new JFrame();
		frame.setSize(width, height);
		frame.setVisible(true);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		JPanel panel = new JPanel();

		Graphics g;

		frame.add(panel);

		System.out.println("erstellt");

		BufferedImage image = null;

		try
		{
			image = new Robot().createScreenCapture(new Rectangle(0, 0, width,
					height));
		}
		catch (AWTException e1)
		{
			e1.printStackTrace();
		}

		while (!Thread.interrupted())
		{

			writer.println("-50");
			writer.flush();

			try
			{

				x++;

				int pixel[][] = (int[][]) in.readObject();

				for (int x = 0; x < (width - 1); x++)
				{
					for (int y = 0; y < (height - 1); y++)
					{
						image.setRGB(x, y, pixel[x][y]);

					}

				}

				y++;

				System.out.println("vor zeichnen");

				g = panel.getGraphics();

				g.drawImage(image, 0, 0, null);

				System.out
						.println("empfangen : " + x + "     gezeichnet: " + y);

			}
			catch (ClassNotFoundException e)
			{
				e.printStackTrace();
			}

		}

	}
}

Client:

Java:
package de.kuehne.writeimage;

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.net.Socket;

import javax.swing.ImageIcon;

public class Client {

	public static void main(String args[]) throws AWTException, IOException
	{

		new Client().los();

	}

	public void los() throws AWTException, IOException
	{

		Robot robot = new Robot();

		Socket sock;

		sock = new Socket("localhost", 5000);

		Rectangle rect = new Rectangle();
		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
		rect.x = 0;
		rect.y = 0;
		rect.width = dim.width;
		rect.height = dim.height;

		ObjectOutputStream ob = new ObjectOutputStream(sock.getOutputStream());

		BufferedReader reader = new BufferedReader(new InputStreamReader(sock
				.getInputStream()));

		int x = 0;

		while (!Thread.interrupted())
		{

			while (!reader.ready())
			{

			}

			if (x == 20)
			{

				x = 0;

				ob.reset();

			}

			String s = reader.readLine();

			if (s.equals("-50"))
			{

				Image image = robot.createScreenCapture(rect);

				ImageIcon images = new ImageIcon(image);

				ob.writeObject(images);
			}

			x++;

		}

	}

}
 

Marco13

Top Contributor
Mit einigen Anpassungen (Port, ImageIcon statt int[][]) gehts. Das Zeichnen sollte eigentlich nur in einer Überschriebenen paintComponent-Methode passieren (nicht getGraphics auf einer Component aufrufen). Inwieweit es dort Probleme geben kann, weil das alles im selben Thread passiert, weiß ich nicht. Jedenfalls könnte ein Grund für die Aussetzer auch sein, dass der Speicher volläuft (schau dir mal die Speicherauslastung an....)
 
M

Mr.Bross

Gast
Tut mir Leid, die Klassen haben leider nicht ganz gestimmt. Ich nehme ein int[][] auch beim Clienten (Ist auf dem anderen Rechner). Und der Port stimmt natürlich auch überein. Probier es mal bitte mit einem int[][]-Array beim Clienten (Und natürlich Server).
 

Marco13

Top Contributor
Hab' gerade nicht so viel Zeit (und bin auch die nächsten Tage nicht so oft hier) aber vielleicht kann man das Problem eingrenzen, indem man es erstmal mit kleineren Bildern (300x300 oder 500x500) testet. Das ganze mal in einem Profiler (VisualVM oder so) laufen lassen könnte auch Rückschlüsse darauf erlauben, wo er denn genau hängt. Das mit dem x==20 hat sich mir aber immernoch nicht erschlossen...?
 
M

Mr.Bross

Gast
Durch x == 20 (genau ooutputs.reset() ) lösche ich den gespeicherten Inhalt des ObjectOutputStreams. Wenn ich das nicht mache, kommt nach einiger Zeit OutOfMemorieException. Und ich werde es mal mit kleineren Bilder prbieren :)
 

Marco13

Top Contributor
Wenn es einen OutOfMemoryError gibt, ist da mit ziemlicher Sicherheit noch irgendwo ein Fehler... Falls es nicht sooo sehr eilt: Ab nächsten Montag könnt' ich nochmal genauer schauen... Ggf. den Thread dann nochmal uppen oder mit dem aktuellen Stand neu aufmachen, falls bis dahin niemand anderes was cleveres dazu sagen kann....
 
M

Mr.Bross

Gast
Hi,

Ich habe mal einen Test gemacht, indem ich beim Clienten und beim Server jedes einzelne Bild abgespeichert habe. Jetzt weiß ich zumindest was das Problem ist:

Der Client macht alle Bilder richtig, doch beim verschicken stimmt etwas nicht. Das Bild bleibt beim Server immer wie das erste (egal wie es sich beim Clienten verändert), bis beim Clienten ObjectOutputStream.reset() aufgerufen wird. Dann stimmt es wieder perfekt überein. (Also dieses eine Bild) Die nächsten sind dann wieder alle wie das, was nach dem reset() verschickt wurde. Erst wenn dann wieder reset() aufgerufen wird, stimmt es wieder usw.

Jetzt habe ich das Problem gefunden, jetzt fehlt nur noch die Lösung :) Nach jedem mal reset() aufrufen kann man fast vergessen, da es immer etwa 4-5 sekunden braucht, bis es das gemacht hat. Dann ist es ja keine richtige Bild-zu-Bild - Übertragung, sondern einfach nur Screenshots...
 
M

Mr.Bross

Gast
Das mache ich nach jedem mal und die Bilder kommen ja auch an (zähle es mit einem int).
 

Marco13

Top Contributor
Also, ich habe jetzt nochmal das, was du zuletzt gepostet hattests, korrigiert und mit 300x300-Bildern über localhost getestet - und das funktioniert bei mir erstmal :bahnhof: Keine Verzögerungen, keine falschen Bilder...

Java:
import java.awt.AWTException;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class RobotServer {

    private int x;
    private int y;

    public static void main(String args[]) throws IOException,
            InterruptedException
    {

        new RobotServer().los();

    }

    public void los() throws IOException, InterruptedException
    {

        ServerSocket sock = new ServerSocket(5005);

        Socket socket = sock.accept();

        ObjectInputStream in = new ObjectInputStream(socket.getInputStream());

        PrintWriter writer = new PrintWriter(socket.getOutputStream());

        int width = 300;
        int height = 300;

        JFrame frame = new JFrame();
        frame.setSize(width, height);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel();

        Graphics g;

        frame.add(panel);

        System.out.println("erstellt");

        BufferedImage image = null;

        try
        {
            image = new Robot().createScreenCapture(new Rectangle(0, 0, width,
                    height));
        }
        catch (AWTException e1)
        {
            e1.printStackTrace();
        }

        while (!Thread.interrupted())
        {

            writer.println("-50");
            writer.flush();

            try
            {

                x++;

                int pixel[][] = (int[][]) in.readObject();

                for (int x = 0; x < (width - 1); x++)
                {
                    for (int y = 0; y < (height - 1); y++)
                    {
                        image.setRGB(x, y, pixel[x][y]);

                    }

                }

                y++;

                System.out.println("vor zeichnen");

                g = panel.getGraphics();

                g.drawImage(image, 0, 0, null);

                System.out
                        .println("empfangen : " + x + "     gezeichnet: " + y);

            }
            catch (ClassNotFoundException e)
            {
                e.printStackTrace();
            }

        }

    }
}

Java:
import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.net.Socket;

import javax.swing.ImageIcon;

public class RobotClient {

    public static void main(String args[]) throws AWTException, IOException
    {

        new RobotClient().los();

    }

    public void los() throws AWTException, IOException
    {

        Robot robot = new Robot();

        Socket sock;

        sock = new Socket("localhost", 5005);

        Rectangle rect = new Rectangle();
        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
        rect.x = 0;
        rect.y = 0;
        rect.width = 300; //dim.width;
        rect.height = 300; //dim.height;

        ObjectOutputStream ob = new ObjectOutputStream(sock.getOutputStream());

        BufferedReader reader = new BufferedReader(new InputStreamReader(sock
                .getInputStream()));

        int x = 0;

        int width = 300;
        int height = 300;

        while (!Thread.interrupted())
        {

            while (!reader.ready())
            {

            }

            if (x == 20)
            {

                x = 0;

                ob.reset();

            }

            String s = reader.readLine();

            if (s.equals("-50"))
            {

                Image image = robot.createScreenCapture(rect);
                BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                bi.getGraphics().drawImage(image,0,0,null);

                int pixel[][] = new int[width][height];

                for (int xx = 0; xx < (width - 1); xx++)
                {
                    for (int y = 0; y < (height - 1); y++)
                    {
                        pixel[xx][y] = bi.getRGB(xx, y);

                    }

                }


                //ImageIcon images = new ImageIcon(image);
                //ob.writeObject(images);
                ob.writeObject(pixel);
                ob.flush();
            }

            x++;

        }

    }

}
 
M

Mr.Bross

Gast
Ja das geht auch bei mir :) Was hat es mit diesem Abschnitt zu tun??

Java:
 BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                bi.getGraphics().drawImage(image,0,0,null);

Liegt es nur an dem?? Was macht der anders :/
 
M

Mr.Bross

Gast
Ich habe einfach immer getRGB an dem BufferedImage angewandt...

Java:
BufferedImage image =new Robot().createScreenCapture(new Rectangle(0, 0, width,
                    height));

Und dann die for-Schleife. War/ist das falsch??
 

Marco13

Top Contributor
Ach, sorry, ich dachte der Robot liefert nur ein Image zurück. Aber eigentlich sollte das mit jeder Art von BufferedImage funktionieren. Woran es genau lag, dass es nicht funktioniert hat, wenn man direkt das BI vom Robot nimmt, weiß ich spontan leider auch nicht ???:L
 
M

Mr.Bross

Gast
Wie bekommt es Teamviewer hin, die Bilder so schnell zu übertragen?? Ich habe es mal mit einem Kumpel getestet und es hat für ein Bild über 10min gedauert... Die Übertragung mit Teamviewer klappt jedoch recht gut...
 

Marco13

Top Contributor
Da könnte man ein ganzes Buch drüber schreiben :D Ein paar der Punkte wurden witer oben ja schon genannt:
- Nur übertragen, was sich auch geändert hat - dafür gibt's etliche potentiell geeignete Datenstrukturen (hashing, KD-Trees, usw)
- Die Bilddaten komprimieren - die meisten Flächen auf einem "normalen" Desktop sind einfarbig
- Etwas schnelleres als den Robot verwende (direktes auslesen des Grafikkartenspeichers per JNI oder so)
- Keine Konvertierung von Image in int[][] und zurück
- Etwas schnelleres als einen ObjectOutputStream verwenden (d.h. einen einfachen OutputStream, mit einem eigenen Protokoll)
- ...
- ...
- ...
 
M

Mr.Bross

Gast
Ist ein komprimiertes Bild kleiner, als das int-array??
Und mit dem nur übertragen was sich geändert hat. Wie soll man das machen?? Ich könnte ein int-array nehmen und alles was gleich ist durch eine -1 kennzeichnen, da wird es aber nicht viel kleiner...
Und wie schreibt man denn ein eigenes Protokoll über einen OutputStream?? Der muss ja dann auch Objekte nehmen... Und wie soll das dadurch schneller werden?? Es bleibt ja das gleiche Objekt über den gleichen OutputStream...
Und mit der schnelligkeit am Clienten (erstellen usw.) bin ich eigentlich zufrieden, braucht nur etwa 5 millisekunden.
 

Marco13

Top Contributor
Ist ein komprimiertes Bild kleiner, als das int-array??

Im allgemeinen: Ja, und zwar deutlich. Im Moment werden ja alle Pixel verschickt. Bei 1000x1000 Pixeln mit RGBA sind das pro Bild ca. 4MB. Wenn man das ganze stattdessen komprimiert, z.B. als PNG, und dann nur "die PNG-Datei" verschickt, sind es - je nachdem ... vielleicht nur 500kb oder noch weniger.

Und mit dem nur übertragen was sich geändert hat. Wie soll man das machen??

Da gäbe es beliebig ausgefeilte Möglichkeiten. Man könnte (vereinfacht gesagt) den aktuellen und den vorheigen Screenshot vergleichen, und dann feststellen, dass sich von dem 1000x1000-Bild nur ein Bereich der größe 100x100 verändert hat. NUR diesen Bereich könnte man dann als PNG übertragen (mit der Zusatzinformation, an welcher Stelle auf dem Bildschirm der Client dieses "Tile" (Kachel) dann malen soll). So ein "Tiling" könnte (!) ohnehin die "gefühlte Geschwindigkeit" verbessern: Wenn man immer das komplette Bild überträgt, braucht das jedesmal sehr lange. Wenn man stattdessen den Bildschirm z.B. in 10x10 Kacheln aufteilt, und die einzeln überträgt, kommt beim Client zumindest schonmal was an - noch nicht das ganze Bild, aber man sieht immerhin schon mal was...


Und wie schreibt man denn ein eigenes Protokoll über einen OutputStream?? Der muss ja dann auch Objekte nehmen... Und wie soll das dadurch schneller werden?? Es bleibt ja das gleiche Objekt über den gleichen OutputStream...

Das bezog sich auf die obigen Punkte: Man weiß nicht genau, was ein ObjectOutputStream genau verschickt, wenn man ihm einen int[][] array gibt. Es wird sicher sehr kompakt und effizient sein, aber stattdessen ein PNG (als Rohdaten, also einfach als ein byte[] array) wird vermutlich schneller sein. Falls man Tiling verwendet, muss man aber zusätzlich noch die Positionen und Größen der gesendeten Tiles mitschicken, damit der Client sie richtig anzeigen kann. Das würde man dann ggf. mit einem eigenen Protokoll machen. Alternativ dazu mit einer Klasse, die einfach "Serializable" ist, und DIE schickt man dann über einen ObjectOutputStream
Code:
class Tile implements Serializable
{
    private int x,y,width,height;
    private byte[] pngData;
    ...
}

Alles nur GANZ grobe Ideen. Da kann man eine Menge Arbeit reinstecken, wenn man es "vernünftig" machen will...
 

ice-breaker

Top Contributor
auch ein PNG wird dich nicht großartig weiterbringen, du müsstest nur den Bereich senden, der sich seit dem letzten Screenshot auch wirklich verändert.
TeamViewer versendet es dann zeilenweise (also jede 4. Zeile, dann jede 3 ...) und erreicht damit dass das Bild scheinbar schneller aufgebaut wird.

Wie bekommt es Teamviewer hin, die Bilder so schnell zu übertragen?? Ich habe es mal mit einem Kumpel getestet und es hat für ein Bild über 10min gedauert... Die Übertragung mit Teamviewer klappt jedoch recht gut...
:eek:
 
M

Mr.Bross

Gast
Danke für die Antworten. Tut mir leid dass ich es erst jetzt lese, aber ich habe die Seite immer aktualisiert und erst jetzt mitbekommen dass eine neue Seite angefangen wurde >.< . Ich werde mal ausprobieren was du geschrieben hast.
 
M

Mr.Bross

Gast
Also wenn ich es richtig verstanden habe, soll ich die Bilder (durchgehend?) vergleichen(mit den RGB-Werten?). Und wenn sich etwas verändert hat, verschicke ich die Daten des Ortes, der sich verändert hat(genaue Position und Länge/Breite) und dann das PNG(nur der Teil vom Screenshot). Und der Server zeichnet dann nur diese Fläche an den richtigen Fleck :).

Wie kann ich jetzt ein PNG-Bild virtuell speichern?? Also ich kann es auf der Festplatte speichern klar, aber virtuell??(wenn man das so nennen kann)
Und verstehe ich das richtig mit dem RGB-vergleich?? Oder wäre es anders besser...
 

Marco13

Top Contributor
Das mit dem RGB-Vergleich wäre eine Möglichkeit. Erstmal wäre das evtl. noch etwas langsam. Wenn man genauer weiß, von welchem Typ das Bild ist, kann man sich die Pixeldaten evtl. direkt als int[] array abholen, womit der Vergleich u.U. schneller werden könnte, aber das ist nur eine Optimierung, die man nachträglich machen kann (sofern man den Vergleich schön in eine eigene Methode gepackt hat).

Um ein Bild "virtuell" zu speichern, kann man einen ByteArrayOutputStream verwenden, grob sowas wie
Java:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
byte pngData[] = baos.toByteArray();
 
M

Mr.Bross

Gast
Hätte da noch eine Frage zum vergleichen. Ich speichere mom jeden momentanen Screenshot in pixel[][] und das Bild zuvor in pixel2[][]. Dann vergleiche ich die beiden. Aber wenn ein Unterschied festgestellt wird, was muss ich dann machen?? Wenn ich dann ein pixel3[][] nehme und dort die Werte drin speichere ist schonmal das erste Problem, dass ich die Größe vorher nicht weiß...
 

Marco13

Top Contributor
Wie schon mehrfach gesagt: Das kann man beliebig ausgefeilt machen. Im einfachsten Fall speichert man sich für seine beiden Arrays die kleinste und größte Position, an der ein Unterschied gefunden wurde
Code:
int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE;
int maxX = -1;
int maxY = -1;
for (x...)
{
    for (y...)
    {
        if (differentAt(x,y))
        {
            minX = Math.min(minX, x);
            ...usw... 
        }
    }
}
Dann hat man am ende Das Rechteck (minX,minY) - (maxX, maxY) in dem "irgendwas" unterschiedlich ist. Das kann man dann übertragen. Natürlich ist das "schlecht", wenn sich zufällig genau der obere Linke und der Untere Rechte Pixel geändert haben: Man überträgt ALLES, obwohl nur 2 Pixel unterschiedlich sind ... da kann man sich dann was überlegen, wie man z.B. rausfinden kann, wie man die Bereiche, die aufgrund einer Änderung übertragen werden, sinnvoll wählt. Z.B. könnte man das Bild in 10x10 Kacheln einteilen, und jede Kachel übertragen, in der sich was geändert hat...
 

Ähnliche Java Themen


Oben