6

次の問題は、画面を監視し、イベントを記録し(測定テキストボックスが緑色に変わります)、それに至るまでのすべてのイベントを記録し、それに至るまでのイベントの「フィルム」を作成する必要があります。残念ながら、画面全体を記録する必要があります。私はこれまで、認識が行われる部分を実行しました。ただし、1秒あたり2フレームしか取得できません。25〜30fps程度にしたいです。

私のアイデアは、2つの別々のスレッドで書き込みと読み取りを行うことでした。書き込みイベントはまれであり、バックグラウンドで実行できるため、記録イベントはより多くの時間を要し、より速く実行される可能性があります。残念ながら、全体が遅すぎるようです。イベントが発生する10〜20秒に画面に画面を書き込めるようにしたいと思います。

編集:可能であれば、私は可能な限りプラットフォームに依存しないようにしたいと思います。

編集2:Xuggler用のプラットフォームに依存しないjarファイルがあるようです。残念ながら、私は自分の目的のためにそれをどのように使用できるかを実際には理解していません:isarecordがトリガーされるポイントまでの20秒を記録します。

これが私がこれまでにしたことです:

package fragrecord;

import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

public class Main {
    public static void main(String[] args) {
        //The numbers are just silly tune parameters. Refer to the API.
        //The important thing is, we are passing a bounded queue.
        ExecutorService consumer = new ThreadPoolExecutor(1,4,30,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(100));
        System.out.println("starting");
        //No need to bound the queue for this executor.
        //Use utility method instead of the complicated Constructor.
        ExecutorService producer = Executors.newSingleThreadExecutor();

        Runnable produce = new Produce(consumer);
        producer.submit(produce);  
        try {
            producer.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        consumer.shutdown();
        try {
            consumer.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

class Produce implements Runnable {
    private final ExecutorService consumer;

    public Produce(ExecutorService consumer) {
        this.consumer = consumer;
    }
    boolean isarecord(BufferedImage image){
        int x=10, y = 10;
        Color c = new Color(image.getRGB(x,y));
        int red = c.getRed();
        int green = c.getGreen();
        int blue = c.getBlue();
        // Determine whether to start recording
        return false;

    }


    @Override
    public void run() {

        Robot robot = null;
        try {
            robot = new Robot();
        } catch (AWTException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //
        // Capture screen from the top left to bottom right
        //
        int i = 0;
        while(true) {

            i++;
        BufferedImage bufferedImage = robot.createScreenCapture(
                new Rectangle(new Dimension(1024, 798)));

        Runnable consume = new Consume(bufferedImage,i);
        consumer.submit(consume);
        }

    }
}

class Consume implements Runnable {
    private final BufferedImage bufferedImage;
    private final Integer picnr;
    public Consume(BufferedImage bufferedImage, Integer picnr){
        this.bufferedImage = bufferedImage;
        this.picnr = picnr;
    }

    @Override
    public void run() {
        File imageFile = new File("screenshot"+picnr+".png");
        try {
            System.out.println("screenshot"+picnr+".png");
            ImageIO.write(bufferedImage, "png", imageFile);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
4

5 に答える 5

4

私はあなたのコードを少し編集しようとしました。ファイルを作成する代わりに、データ圧縮のオーバーヘッド時間を取り除くファイルをpng作成しようとしましたが、ディスク容量が犠牲になりました。bmp

結果: fps の数え方はわかりませんが、解決策はあなたのものよりも高速です。:-)

于 2012-09-10T07:56:53.750 に答える
3

どのくらいの時間robot.createScreenCapture()がかかるかを測定する必要があります。40 ミリ秒以上かかる可能性があります。これは、純粋な Java で目的を達成する方法がないことを意味します。私の経験では、通話は非常に遅くなる可能性があります。

私はトリックでその時間をかなり短縮することができましたが、それは Unix でのみ機能します: VNC サーバー (= RAM のデスクトップ) を起動します。TightVNCのソース コードにパッチを適用して、NIO を使用し、メモリ マップ ファイルを使用してイメージをディスクに書き込みました。それは私に約10-20 fpsを与えました。

NIO を使用してイメージを書き込むコードは次のとおりです。

private File pixelFile = new File("tmp", "pixels.nio").getAbsoluteFile();
private IntBuffer intBuffer;
private FileChannel rwChannel;

private MappedByteBuffer byteBuffer;
private int[] pixels;

private void createMemoryMappedFile() {
    File dir = pixelFile.getParentFile();
    if(!dir.exists()) {
        dir.mkdirs();
    }

    try {
        rwChannel = new RandomAccessFile(pixelFile, "rw").getChannel();

        int width = ...;
        int height = ...;
        pixels = new int[width*height];

        byteBuffer = rwChannel.map(MapMode.READ_WRITE, 0, width * height * 4);
        intBuffer = byteBuffer.asIntBuffer();
    } catch(Exception e) {
        throw new RuntimeException("Error creating NIO file " + pixelFile, e);
    }
}

public void saveImage() {

     buffer.position(0);
     buffer.put(image.getRaster().getPixels(0,0,width,height,pixels));

     flushPixels();
}

private void flushPixels() {
    byteBuffer.force();
}
于 2012-09-10T08:07:49.433 に答える
3

あなたの最大の問題は、実際に画像を作成するためのスレッドが 1 つしかないことです。ThreadPoolExecutorは、期待どおりにスレッドを作成しません。

javadoc から

  • 実行中のスレッドが corePoolSize よりも少ない場合、Executor は常に、キューに入れるよりも新しいスレッドを追加することを優先します。
  • corePoolSize 以上のスレッドが実行されている場合、Executor は常に、新しいスレッドを追加するよりも要求をキューに入れることを優先します。
  • 要求をキューに入れることができない場合、これが maximumPoolSize を超えない限り、新しいスレッドが作成されます。この場合、タスクは拒否されます。

そのため、キューがいっぱいでない限り、1 つのスレッドのみを使用します。この時点で、メモリに 100 個のスクリーンショットがあり、GC に作業が追加されています。コア スレッドを 4 に設定し (私のラップトップには 4 つのコアがあります)、メモリを 1 GB に増やすと、20 FPS 程度をキャプチャできます。

ディスクへの出力が制限されている場合は、最後に書き込まれた 400 個のイメージをバイト配列としてキューに保存し、ボタンが「緑色」になったときにのみディスクに書き込むことができます。ただし、私のテストでは、これらのイメージは 100MB 以上の RAM を必要とするため、十分なメモリがあることを確認してください。

于 2012-09-10T08:08:08.713 に答える
2

エグゼキューターを使用するさまざまな理由により、nio への書き換えなどは役に立ちません。

考慮すべき事項を次に示します。

  1. Java でのイメージ キャプチャは低速です。JNI の依存関係がなければ、何もできません。
  2. png の代わりに jpeg を使用すると、はるかに高速になります
  3. ImageIO を使用した画像圧縮は低速です。古い学校の独自の JpegEncoder クラス (com.sun パッケージ内) または TurboJPEG Java ライブラリ (JNI も) を使用できますが、ボトルネックではありません。
  4. ディスク I/O は間違いなく問題ではありません (5 mb/s 未満で書き込みを行っているため、心配する必要はありません)
  5. 多くの画像を並行して書き込むと、実際にはアプリケーションの速度が低下しますが、速度は上がりません (ssd がない場合)。
  6. 代わりに、キャプチャ/分析スレッドを並列化することを検討してください (たとえば、20 番目のフレームごとにのみ分析を行う) (*)
  7. Java アプリを最適化して 25fps (100% cpu xD を使用) で実行できるようにする前に、ネイティブ言語を使用してプラットフォームごとにこのアプリを 2 回書き終えることになるでしょう。
  8. ハイブリッド ソリューションも真剣に検討してください。たとえば、利用可能なツールを使用してすべてを圧縮ムービーに記録し、後で分析を行います (**)

一言で言えば、Javaはあなたがやりたいことを本当にひどく嫌っています。

それにもかかわらず、私はこのツールの独自のバージョンを作成しました。 http://pastebin.com/5h285fQw

それが行うことの1つは、マウスに続く小さな長方形を記録できるようにすることです。500x500 では、バックグラウンドで画像が書き込まれ、簡単に 25 fps に到達します (画像の圧縮 + 書き込みには 5 ~ 10 ミリ秒かかるため、記録よりもはるかに高速に書き込みます)。


(*) 画像の分析方法については語っていませんが、これがパフォーマンスの問題の主な原因のようです。いくつかのアイデア:

  • N 番目のフレームごとにのみ見る
  • 画面の一部のみをキャプチャし、その部分を時間の経過とともに移動します (後で画像を再構成します。これによりひどいテアリングが発生しますが、目的には関係ない可能性があります)。
  • キャプチャ自体は「遅すぎない」必要があります(フルスクリーンではおそらく10〜20fps)。シリアル キャプチャを使用するが、分析を並列化する

(**) macosx では、組み込みの quicktime x を使用して、非常に効率的に hdd に記録できます。Windowsでは、playclaw(http://www.playclaw.com/)は非常に優れており、おそらくお金の価値があると聞きました(Javaビーストの最適化に無駄な時間を費やして何を作るか考えてみてください:))

于 2012-09-18T23:33:52.810 に答える
1

トピックから外れますが、Xugglerを見てください。Java でビデオを作成する場合に便利です。

編集:また、各画像をディスクにダンプせずにバイト配列に追加し、めったにディスクにダンプしない場合は、イメージ コンシューマー コードを最適化できます。

編集 2:ライブラリの「インストールなし」バージョンとそれに対する maven 依存関係 (プリコンパイルされたプラットフォーム固有のライブラリを含む jar) があります: blog.xuggle.com/2012/04/03/death-to-installersおよびxuggle。 com/xuggler/downloads

于 2012-09-10T07:26:53.837 に答える