Saya baru dalam Pemrosesan dan saya perlu membuat program yang, menangkap monitor utama, menunjukkan pada layar kedua warna rata-rata dan membuat spiral menggunakan warna lain (warna dominan perseptual) yang didapat oleh suatu fungsi.
Masalahnya adalah programnya sangat lambat (lag, 1FPS). Saya pikir itu karena terlalu banyak hal yang harus dilakukan setiap kali saya melakukan tangkapan layar, tetapi saya tidak tahu bagaimana membuatnya lebih cepat.

Juga mungkin ada banyak masalah lain, tetapi yang utama adalah itu.
Terima kasih banyak!

Berikut kodenya:

import java.awt.Robot;
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;

PImage screenshot; 

float a = 0;
int blockSize = 20;

int avg_c;
int per_c;


void setup() {
  fullScreen(2); // 1920x1080
  noStroke();
  frame.removeNotify();
}

void draw() { 
  screenshot();
  avg_c = extractColorFromImage(screenshot);
  per_c = extractAverageColorFromImage(screenshot);
  background(avg_c); // Average color
  spiral();
}


void screenshot() {
  try{
    Robot robot_Screenshot = new Robot();
    screenshot = new PImage(robot_Screenshot.createScreenCapture
    (new Rectangle(0, 0, displayWidth, displayHeight)));
  }
  catch (AWTException e){ }
  frame.setLocation(displayWidth/2, 0);
}

void spiral() {
  fill (per_c); 
  for (int i = blockSize; i < width; i += blockSize*2)
  {
    ellipse(i, height/2+sin(a+i)*100, blockSize+cos(a+i)*5, blockSize+cos(a+i)*5);    
    a += 0.001;
  }
}


color extractColorFromImage(PImage screenshot) { // Get average color
    screenshot.loadPixels(); 
    int r = 0, g = 0, b = 0; 
    for (int i = 0; i < screenshot.pixels.length; i++) { 
        color c = screenshot.pixels[i]; 
        r += c>>16&0xFF; 
        g += c>>8&0xFF; 
        b += c&0xFF;
    } 
    r /= screenshot.pixels.length; g /= screenshot.pixels.length; b /= screenshot.pixels.length;
    return color(r, g, b);
}

color extractAverageColorFromImage(PImage screenshot) { // Get lab average color (perceptual)
  float[] average = new float[3];
  CIELab lab = new CIELab();

  int numPixels = screenshot.pixels.length;
  for (int i = 0; i < numPixels; i++) {
    color rgb = screenshot.pixels[i];

    float[] labValues = lab.fromRGB(new float[]{red(rgb),green(rgb),blue(rgb)});

    average[0] += labValues[0];
    average[1] += labValues[1];
    average[2] += labValues[2];
  }

  average[0] /= numPixels;
  average[1] /= numPixels;
  average[2] /= numPixels;

  float[] rgb = lab.toRGB(average);

  return color(rgb[0] * 255,rgb[1] * 255, rgb[2] * 255);
}


public class CIELab extends ColorSpace {

    @Override
    public float[] fromCIEXYZ(float[] colorvalue) {
        double l = f(colorvalue[1]);
        double L = 116.0 * l - 16.0;
        double a = 500.0 * (f(colorvalue[0]) - l);
        double b = 200.0 * (l - f(colorvalue[2]));
        return new float[] {(float) L, (float) a, (float) b};
    }

    @Override
    public float[] fromRGB(float[] rgbvalue) {
        float[] xyz = CIEXYZ.fromRGB(rgbvalue);
        return fromCIEXYZ(xyz);
    }

    @Override
    public float getMaxValue(int component) {
        return 128f;
    }

    @Override
    public float getMinValue(int component) {
        return (component == 0)? 0f: -128f;
    }    

    @Override
    public String getName(int idx) {
        return String.valueOf("Lab".charAt(idx));
    }

    @Override
    public float[] toCIEXYZ(float[] colorvalue) {
        double i = (colorvalue[0] + 16.0) * (1.0 / 116.0);
        double X = fInv(i + colorvalue[1] * (1.0 / 500.0));
        double Y = fInv(i);
        double Z = fInv(i - colorvalue[2] * (1.0 / 200.0));
        return new float[] {(float) X, (float) Y, (float) Z};
    }

    @Override
    public float[] toRGB(float[] colorvalue) {
        float[] xyz = toCIEXYZ(colorvalue);
        return CIEXYZ.toRGB(xyz);
    }

    CIELab() {
        super(ColorSpace.TYPE_Lab, 3);
    }

    private double f(double x) {
        if (x > 216.0 / 24389.0) {
            return Math.cbrt(x);
        } else {
            return (841.0 / 108.0) * x + N;
        }
    }

    private double fInv(double x) {
        if (x > 6.0 / 29.0) {
            return x*x*x;
        } else {
            return (108.0 / 841.0) * (x - N);
        }
    }


    private final ColorSpace CIEXYZ =
        ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);

    private final double N = 4.0 / 29.0;
}
1
Kevin 5 Januari 2021, 21:31

2 jawaban

Ya, sekitar 1 FPS di mesin saya:

About 1 FPS

Untuk mengoptimalkan kode bisa sangat sulit, jadi alih-alih membaca semuanya mencari hal untuk ditingkatkan, saya mulai dengan menguji di mana Anda kehilangan begitu banyak kekuatan pemrosesan. Jawabannya ada di baris ini:

per_c = extractAverageColorFromImage(screenshot);

Metode extractAverageColorFromImage ditulis dengan baik, tetapi metode ini meremehkan jumlah pekerjaan yang harus dilakukan. Ada hubungan kuadrat antara ukuran layar dan jumlah piksel di layar ini, jadi semakin besar layar semakin buruk situasinya. Dan metode ini memproses setiap piksel tangkapan layar sepanjang waktu, beberapa kali per tangkapan layar.

Ini banyak pekerjaan untuk warna rata-rata. Sekarang, jika ada cara untuk memotong beberapa sudut... mungkin layar yang lebih kecil, atau tangkapan layar yang lebih kecil... oh! ada! Mari kita ubah ukuran tangkapan layar. Lagi pula, kita tidak perlu membahas detail seperti piksel individual untuk rata-rata. Dalam metode screenshot, tambahkan baris ini:

void screenshot() {
  try {
    Robot robot_Screenshot = new Robot();
    screenshot = new PImage(robot_Screenshot.createScreenCapture(new Rectangle(0, 0, displayWidth, displayHeight)));
    // ADD THE NEXT LINE
    screenshot.resize(width/4, height/4);
  }
  catch (AWTException e) {
  }
  frame.setLocation(displayWidth/2, 0);
}

Saya membagi beban kerja dengan 4, tetapi saya mendorong Anda untuk mengubah angka ini sampai Anda mendapatkan hasil memuaskan tercepat yang Anda bisa. Ini hanya bukti konsep:

10x faster!

Seperti yang Anda lihat, mengubah ukuran tangkapan layar dan membuatnya 4x lebih kecil memberi saya kecepatan 10x lebih banyak. Itu bukan keajaiban, tetapi jauh lebih baik, dan saya tidak dapat melihat perbedaan pada hasil akhirnya - tetapi tentang bagian itu, Anda harus menggunakan penilaian Anda sendiri, karena Andalah yang tahu tentang proyek Anda. . Semoga bisa membantu!

Selamat bersenang-senang!

3
laancelot 5 Januari 2021, 19:08

Sayangnya saya tidak bisa memberikan jawaban yang detail seperti laancelot (+1), tapi semoga saya bisa memberikan beberapa tips:

  1. Mengubah ukuran gambar jelas merupakan arah yang baik. Ingatlah bahwa Anda juga dapat melewati sejumlah piksel alih-alih menambah setiap piksel. (jika Anda menangani indeks piksel dengan benar, Anda bisa mendapatkan efek serupa untuk mengubah ukuran tanpa memanggil pengubahan ukuran, meskipun itu tidak akan menghemat banyak waktu CPU Anda)
  2. Jangan membuat instance Robot baru beberapa kali dalam satu detik. Buat sekali dalam pengaturan dan gunakan kembali. (Ini lebih merupakan kebiasaan yang baik untuk dilakukan)
  3. Gunakan profiler CPU, seperti yang ada di VisualVM untuk melihat apa yang sebenarnya lambat dan bertujuan untuk mengoptimalkan yang paling lambat barang dulu.

Contoh poin 1:

for (int i = 0; i < numPixels; i+= 100)

Contoh poin 2:

Robot robot_Screenshot;
...
void setup() {
  fullScreen(2); // 1920x1080
  noStroke();
  frame.removeNotify();
  try{
    robot_Screenshot = new Robot();
  }catch(AWTException e){
    println("error setting up screenshot Robot instance");
    e.printStackTrace();
  }
}
...
void screenshot() {
  screenshot = new PImage(robot_Screenshot.createScreenCapture
    (new Rectangle(0, 0, displayWidth, displayHeight)));
  frame.setLocation(displayWidth/2, 0);
}

contoh poin 3: VisualVM CPU profiler

George Profenza 5 Januari 2021, 23:02