Initial sharing of project

master
Alexander Kimmig 2023-10-20 16:07:01 +02:00
commit 2b6277cef2
31 changed files with 781 additions and 0 deletions

BIN
ChartDirector_s.jar Normal file

Binary file not shown.

81
Image.java Normal file
View File

@ -0,0 +1,81 @@
import java.util.ArrayList;
import java.io.File;
import java.awt.Color;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
/**
* Beschreiben Sie hier die Klasse Image.
*
* @author Alexander Kimmig
* @version 1.0
*/
public class Image
{
/**
* Liest eine JPG-Bilddatei ein und gibt diese als ArrayList von RGB-Objekten zurück
*
* @param filename Dateiname die eingelesen werden soll
* @return ArrayList<RGB> der einzelnen Pixelwerte
* @throws Exception
*/
public static ArrayList<RGB> readFile(String filename) throws Exception {
ArrayList<RGB> points = new ArrayList<>();
File file= new File(filename);
BufferedImage img = ImageIO.read(file);
for (int y = 0; y < img.getHeight(); y++) {
for (int x = 0; x < img.getWidth(); x++) {
points.add(new RGB(img.getRGB(x,y)));
}
}
return points;
}
/**
* Schreibt die RGB-Daten in eine Datei
*
* @param input Dateiname der ursprünglichen Datei (wird für Dimensionen gebraucht)
* @param output Dateiname der neuen Datei
* @param data RGB-Daten der einzelnen Pixel
*/
public static void writeFile(String input, String output, ArrayList<RGB> data) throws Exception {
File file = new File(input);
BufferedImage img = ImageIO.read(file);
int w = img.getWidth();
int y = 0, x = 0;
for (RGB p : data) {
img.setRGB(x, y, p.toInt());
if (++x >= w) {
x = 0;
++y;
}
}
File f = new File(output);
ImageIO.write(img, "jpg", f);
}
/**
* Schreibt die RGB-Daten in eine Datei anhand der Quantifizierungs-Punkte
* Jeder Pixel bekommt die Farbe des nächstliegenden Quantifizierungs-Punktes.
*
* @param input Dateiname der ursprünglichen Datei (wird für Dimensionen gebraucht)
* @param output Dateiname der neuen Datei
* @param data RGB-Daten der einzelnen Pixel
* @param quants Quantifizierungs-Punkte
*/
public static void writeFile(String input, String target, ArrayList<RGB> data, ArrayList<RGB> quants) throws Exception {
ArrayList<RGB> q = new ArrayList<>();
// Mappe punkte auf quantifizierte Punkte
for (RGB p : data) {
q.add(Quant.findNearest(quants, p));
}
// schreibe Datei
writeFile(input, target, q);
}
}

200
Quant.java Normal file
View File

@ -0,0 +1,200 @@
import java.util.ArrayList;
import java.util.Collections;
/**
* Funktionen, um quantifizierungs-Punkte zu finden
*
* @author Alexander Kimmig
* @version 1.0
*/
public class Quant
{
/**
* Führt eine vollständige RGB-Qunatfizierung durch, Lädt die Datei
* input.jpg im Projektordner und speichert das Ergebnis in output.jpg
*
* @param count Anzahl, wie viele Quantifizierungs-Punkte gefunden werden solle
* @param epoch Anzahl, wie viele Epochen durchlaufen werden sollen
* @throws Exception falls Datei nicht existiert
*/
public static void quantize(int count, int epoch) throws Exception {
ArrayList<RGB> ipoints = Image.readFile("./input.jpg");
ArrayList<RGB> quants = findQuants(count, epoch, ipoints);
for (int i = 0; i < ipoints.size(); i++) {
RGB p = ipoints.get(i);
RGB t = findNearest(quants, p);
p.r = t.r;
p.g = t.g;
p.b = t.b;
}
Image.writeFile("./input.jpg", "./output.jpg", ipoints);
}
/**
* Findet Quantenpunkte
*
* @param count Anzahl, wie viele Quantifizierungs-Punkte gefunden werden solle
* @param epoch Anzahl, wie viele Epochen durchlaufen werden sollen
* @param ipoints RGB-Datenpunkte des Bildes
*
* @return ArrayList<RGB> mit den Quantenpunkten
* @throws Exception falls Datei nicht existiert
*/
public static ArrayList<RGB> findQuants(int count, int epoch, ArrayList<RGB> ipoints) throws Exception {
ArrayList<RGB> qpoints = new ArrayList<>();
// Füge neue, zufällige Punkte hinzu
for (int i = 0; i < count; i++) {
qpoints.add(new RGB(true));
}
for (int i = 0; i < epoch; i++) {
// Epoche durchlaufen
doEpoch(ipoints, qpoints);
// nich bewegte Quantenpunkte versetzen
if (i < epoch-1) resetQuants(qpoints);
}
return qpoints;
}
/**
* Epoche durchlaufen
*
* @param ipoints RGB-Bildpunkte
* @param qpoints bisherige Quantenpunkte -> werden direkt verändert
*/
public static void doEpoch(ArrayList<RGB> ipoints, ArrayList<RGB> qpoints) {
/*
* WICHTIG: Bildpunkte mischen, da ansonsten die Punkte linear durchgegangen werden.
* Bei großen Flächen am Ende (rechts unten) werden sonst viele Quantenpunkte in diesen
* Bereich gezogen
*
* Ausprobieren: deaktivieren und das Beispielbild "sonnenaufgang"
*/
Collections.shuffle(ipoints);
/*
* Eine Epoche: alle Bildpunkte einmal durchlaufen
*/
for (RGB t : ipoints) {
// jeweils den nächstliegenden Quantenpunkt finden
RGB q = findNearest(qpoints, t);
// gegangener Weg speichern
q.walk += t.diff2(q);
q.count++;
// den Punkt verschieben
q.r = (q.r + t.r) / 2;
q.g = (q.g + t.g) / 2;
q.b = (q.b + t.b) / 2;
}
for (RGB tmp : qpoints) {
// falls Punkt verschoben: berechne mittlere gegangene Weglänge
if (tmp.count > 0) {
tmp.walk = Math.max(1, tmp.walk / tmp.count);
}
}
}
/**
* Versetzt die Quantenpunkte, die im letzten Schritt nicht versetzt wurden, d.h.
* diejenigen, die zu weit von der Punktwolke entfernt sind.
*
* Hierfür setze diejenigen, die nicht gegangen sind in die Nähe derjeniger Punkte,
* die besonders weit gelaufen sind (da dort ggf. underfitted)
*
* @param qpoints Quantenpunkte
* @return int wie viele Punkte wurden versetzt
*/
public static int resetQuants(ArrayList<RGB> qpoints) {
int reset = 0;
for (int i = 0; i < qpoints.size(); i++) {
// finde denjenigen Punkt, der am wenigsten gelaufen ist
RGB min = findMinWalk(qpoints);
// wenn es keinen gibt oder dieser auch weit gelaufen ist, dann breche ab
if (min == null || min.walk > 5) break;
// finde denjenigen, der am weitesten gelaufen ist (dessen Bereich underfitted ist)
RGB max = findMaxWalk(qpoints);
// breche ab, falls dieser insgesamt auch sehr wenig gelaufen ist
if (max.walk < 25) break;
// passe den Wert an, damit dieser beim nächsten Schleifendurchlauf nicht mehr direkt gefunden wird
max.walk -= 36;
// setze den Nichtläufer zum Weitläufer
min.setTo(max);
// und variiere dessen Positionen um maximal ±5
min.jitter(5);
// walk = -1 -> nicht mehr weiter in diesem Durchlauf betrachten (nicht mehr als Minimum finden)
min.walk = -1;
// zähle die Anzahl der versetzten Punkte
reset++;
}
// setze gelaufene Wege aller Punkte zurück
for (RGB q : qpoints) {
q.walk = q.count = 0;
}
return reset;
}
/**
* Finde denjenigen Punkt in der Liste, der am wenigsten weit gelaufen ist
*
* @param quants Punktliste
* @return RGB der Punkt, der am wenigsten gelaufen ist oder null
*/
private static RGB findMinWalk(ArrayList<RGB> quants) {
RGB min = null;
for (RGB tmp : quants) {
if (tmp.walk >= 0 && (min == null || tmp.walk < min.walk)) {
min = tmp;
}
}
return min;
}
/**
* Finde denjenigen Punkt in der Liste, der am weitesten gelaufen ist
*
* @param quants Punktliste
* @return RGB der Punkt, der am weitesten gelaufen ist
*/
private static RGB findMaxWalk(ArrayList<RGB> quants) {
RGB max = quants.get(0);
for (RGB q : quants) {
if (q.walk > max.walk) max = q;
}
return max;
}
/**
* Finde den Punkt in der Liste der die kürzeste Distanz zum Zielpunkt hat
*
* @param points Liste der Punkte
* @param target Zielpunkt
* @return RGB Punkt mit kürzestem Abstand
*/
public static RGB findNearest(ArrayList<RGB> points, RGB target) {
RGB nearest = points.get(0);
int mind = target.diff2(nearest);
for (RGB p : points) {
int d = target.diff2(p);
if (d < mind) {
nearest = p;
mind = d;
}
}
return nearest;
}
}

14
README.TXT Normal file
View File

@ -0,0 +1,14 @@
------------------------------------------------------------------------
Dies ist die README-Datei des Projekts. Hier sollten Sie Ihr Projekt
beschreiben.
Erzählen Sie dem Leser (jemand, der nichts über dieses Projekt weiss),
alles, was er/sie wissen muss. Üblicherweise sollte der Kommentar
zumindest die folgenden Angaben umfassen:
------------------------------------------------------------------------
PROJEKTBEZEICHNUNG:
PROJEKTZWECK:
VERSION oder DATUM:
WIE IST DAS PROJEKT ZU STARTEN:
AUTOR(EN):
BENUTZERHINWEISE:

161
RGB.java Normal file
View File

@ -0,0 +1,161 @@
import java.awt.Color;
/**
* Repräsentiert einen Punkt im RGB-Raum
*
* @author Alexander Kimmig
* @version 1.0
*/
public class RGB
{
/**
* Rot-Anteil 0-255
*/
public int r;
/**
* Grün-Anteil 0-255
*/
public int g;
/**
* Blau-Anteil 0-255
*/
public int b;
/**
* Wie weit ist der Punkt in der letzten Epoche gelaufen
*/
public int walk;
/**
* Wie oft ist der Punkt in der letzten Epoche gelaufen
*/
public int count;
/**
* Konstruktor
*
* @param random Punkt zufällig setzen
*/
public RGB(boolean random) {
this(0, 0, 0);
if (random) this.setRandom();
}
/**
* Konstruktor
*
* @param c ein awt.Color-Wert
*/
public RGB(Color c) {
this(c.getRed(), c.getGreen(), c.getBlue());
}
/**
* Konstruktor
*
* @param c ein int-Wert 0xRRGGBB
*/
public RGB(int c) {
this(c >> 16 & 0xFF, c >> 8 & 0xFF, c & 0xFF);
}
/**
* Konstruktor
*
* @param r Rot-Anteil 0-255
* @param g Grün-Anteil 0-255
* @param b Blau-Anteil 0-255
*/
public RGB(int r, int g, int b) {
this.r = Math.min(255, Math.max(0, r));
this.g = Math.min(255, Math.max(0, g));
this.b = Math.min(255, Math.max(0, b));
this.walk = 0;
this.count = 0;
}
/**
* Kopier-Konstruktor
*/
public RGB(RGB copy) {
this(copy.r, copy.g, copy.b);
}
/**
* Ausgabe als String
*
* @return String: "R, G, B"
*/
public String toString() {
return this.r + "," + this.g + "," + this.b;
}
/**
* Ausgabe als awt.Color
*
* @return awt.Color
*/
public Color toColor() {
return new Color(this.r, this.g, this.b);
}
/**
* Ausgabe als int
*/
public int toInt() {
return (this.r << 16) + (this.g << 8) + (this.b);
}
/**
* Setze zufällige Koordinaten
*/
public void setRandom() {
this.r = (int)(Math.random() * 256);
this.g = (int)(Math.random() * 256);
this.b = (int)(Math.random() * 256);
}
/**
* Setze Koordinaten
*/
public void setTo(RGB p) {
this.r = p.r;
this.g = p.g;
this.b = p.b;
}
/**
* Variiere Koordinaten ±max
*
* @param max maximale verschiebung pro Achse
*/
public void jitter(int max) {
this.r = Math.min(255, Math.max(0, this.r + ((int)(Math.random() * max * 2 - max))));
this.g = Math.min(255, Math.max(0, this.g + ((int)(Math.random() * max * 2 - max))));
this.b = Math.min(255, Math.max(0, this.b + ((int)(Math.random() * max * 2 - max))));
}
/**
* Berechne den quadratischen Abstand zweier Punkte
*
* @param a erster Punkt
* @param b zweiter Punkt
*/
public static int diff2(RGB a, RGB b) {
int dr = a.r - b.r;
int dg = a.g - b.g;
int db = a.b - b.b;
return dr*dr + dg*dg + db*db;
}
/**
* Berechne den quadratischen Abstand zu einem anderen Punkt
*/
public int diff2(RGB b) {
return RGB.diff2(this, b);
}
}

251
Window.java Normal file
View File

@ -0,0 +1,251 @@
import ChartDirector.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.io.File;
import java.util.Collections;
/**
* Zeigt das Fenster an mit allen Komponenten
*
* @author Alexander Kimmig
* @version 1.0
*/
public class Window implements ActionListener
{
private JFrame frame;
private JPanel drawPanel;
private ArrayList<RGB> image;
private ArrayList<RGB> orig;
private ArrayList<RGB> quants;
private int alpha = 25;
private int beta = 45;
private JButton vl;
private JButton vr;
private JButton vu;
private JButton vd;
private JButton b_load;
private JButton b_gen;
private JButton b_epoc;
private JButton b_reset;
private JButton b_save;
private JComboBox s_input;
private JTextField t_anz;
private JTextField t_output;
/**
* Konstruktor: legt JFrame und alle Panels/Buttons an
*/
public Window()
{
frame = new JFrame();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }
});
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
drawPanel = new JPanel();
drawPanel.setPreferredSize(new Dimension(1000, 1000));
drawPanel.setBackground(new Color(0xFFFFFF));
JPanel view = new JPanel();
JPanel steps = new JPanel();
JPanel config = new JPanel();
mainPanel.add(drawPanel);
mainPanel.add(view);
mainPanel.add(steps);
mainPanel.add(config);
vl = new JButton("<");
vl.addActionListener(this);
view.add(vl);
vr = new JButton(">");
vr.addActionListener(this);
view.add(vr);
vu = new JButton("^");
vu.addActionListener(this);
view.add(vu);
vd = new JButton("v");
vd.addActionListener(this);
view.add(vd);
b_load = new JButton("Datei laden");
b_load.addActionListener(this);
steps.add(b_load);
b_gen = new JButton("Zufällige Punkte erzeugen");
b_gen.addActionListener(this);
steps.add(b_gen);
b_epoc = new JButton("Epoche durchspielen");
b_epoc.addActionListener(this);
steps.add(b_epoc);
b_reset = new JButton("Punkte versetzen");
b_reset.addActionListener(this);
steps.add(b_reset);
b_save = new JButton("Datei speichern");
b_save.addActionListener(this);
steps.add(b_save);
config.add(new JLabel("Input:"));
s_input = new JComboBox(getJPGs());
config.add(s_input);
config.add(new JLabel("Anzahl Punkte:"));
t_anz = new JTextField("32");
t_anz.setPreferredSize(new Dimension(100,20));
config.add(t_anz);
config.add(new JLabel("Output:"));
t_output = new JTextField("output.jpg");
t_output.setPreferredSize(new Dimension(100,20));
config.add(t_output);
frame.add(mainPanel);
frame.pack();
frame.setVisible(true);
this.image = new ArrayList<>();
this.quants = new ArrayList<>();
this.plot();
}
/**
* plottet die Punkte in ein 3D-Scatter-Plot und zeigt dieses an
*/
public void plot()
{
ChartViewer viewer = new ChartViewer();
// Einstellungen setzen
ThreeDScatterChart c = new ThreeDScatterChart(1000, 1000);
c.setPlotRegion(500, 480, 600, 600, 450);
c.xAxis().setTitle("R", "Arial Bold", 10);
c.yAxis().setTitle("G", "Arial Bold", 10);
c.zAxis().setTitle("B", "Arial Bold", 10);
c.xAxis().setDateScale(0, 255);
c.yAxis().setDateScale(0, 255);
c.zAxis().setDateScale(0, 255);
c.setViewAngle(alpha, beta);
// Bildpunkte
double[] x = new double[image.size()];
double[] y = new double[image.size()];
double[] z = new double[image.size()];
for (int i = 0; i < image.size(); i++) {
x[i] = image.get(i).r;
y[i] = image.get(i).g;
z[i] = image.get(i).b;
}
// Koordinaten Form Größe Farbe ARGB Rahmen ARGB
c.addScatterGroup(x,y,z, "", Chart.CircleShape, 5, 0xE0FF0000, 0xFF000000);
// Quantenpunkte
x = new double[quants.size()];
y = new double[quants.size()];
z = new double[quants.size()];
for (int i = 0; i < quants.size(); i++) {
x[i] = quants.get(i).r;
y[i] = quants.get(i).g;
z[i] = quants.get(i).b;
}
c.addScatterGroup(x,y,z, "", Chart.CircleShape, 12);
viewer.setChart(c);
drawPanel.removeAll();
drawPanel.add(viewer);
frame.pack();
}
/**
* Lies die vorhandenen JPG-Dateien aus dem Ordner inputs aus
*
* @return String[] Dateinamen
*/
private String[] getJPGs() {
ArrayList<String> result = new ArrayList<String>();
File d = new File("./inputs/");
File[] list = d.listFiles();
for(int i=0; i<list.length; i++) {
String n = list[i].getName();
// nur JPG-Bilder
if (n.matches(".*\\.[jJ][pP][eE]?[gG]$")) {
result.add(n);
}
}
String[] ret = new String[result.size()];
return result.toArray(ret);
}
/**
* Button-Reaktivität
*
* @param e ActionEvent
*/
public void actionPerformed(ActionEvent e) {
/**
* Pfeilbuttons: Ändern der Ansicht
*/
if (e.getSource() == this.vr) beta = (beta + 345) % 360;
if (e.getSource() == this.vl) beta = (beta + 15) % 360;
if (e.getSource() == this.vu) alpha = (alpha + 345) % 360;
if (e.getSource() == this.vd) alpha = (alpha + 15) % 360;
/**
* Datei laden und RGB-Daten in ArrayList speichern
*/
if (e.getSource() == this.b_load) {
try {
this.orig = Image.readFile("./inputs/" + (String)this.s_input.getSelectedItem());
for (RGB p : this.orig) {
// kopiere die originalen Daten, damit die Liste später gemischt werden kann
this.image.add(p);
}
} catch (Exception ex) {}
}
/**
* Zufällige Punkte erzeugen
*/
if (e.getSource() == this.b_gen) {
this.quants = new ArrayList<>();
for (int i = 0; i < Integer.parseInt(this.t_anz.getText()); i++) {
this.quants.add(new RGB(true));
}
}
/**
* Epoche durchlaufen
*/
if (e.getSource() == this.b_epoc) Quant.doEpoch(this.image, this.quants);
/**
* Nicht bewegte Punkte versetzen
*/
if (e.getSource() == this.b_reset) Quant.resetQuants(this.quants);
/**
* quantifiziertes Bild speichern
*/
if (e.getSource() == this.b_save) {
try {
Image.writeFile("./inputs/" + (String)this.s_input.getSelectedItem(), "./outputs/" + this.t_output.getText(), this.orig, this.quants);
} catch (Exception ex) {}
}
// neue Anzeige rendern
this.plot();
}
}

BIN
inputs/ara.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

BIN
inputs/gras.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
inputs/jaguar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

BIN
inputs/jaguar_klein.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
inputs/kaiman.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
inputs/sonnenaufgang.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
outputs/ara32.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
outputs/gras32.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
outputs/gras64.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
outputs/jaguar16-2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
outputs/jaguar16.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
outputs/jaguar2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
outputs/jaguar3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
outputs/jaguar4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
outputs/jaguar5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
outputs/jaguar6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
outputs/jaguar64.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
outputs/kaiman32-2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
outputs/kaiman32-3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
outputs/kaiman32.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
outputs/output.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
outputs/output_groß32.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
outputs/sonne.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

74
package.bluej Normal file
View File

@ -0,0 +1,74 @@
#BlueJ package file
dependency1.from=Image
dependency1.to=RGB
dependency1.type=UsesDependency
dependency2.from=Image
dependency2.to=Quant
dependency2.type=UsesDependency
dependency3.from=Quant
dependency3.to=RGB
dependency3.type=UsesDependency
dependency4.from=Quant
dependency4.to=Image
dependency4.type=UsesDependency
dependency5.from=Window
dependency5.to=RGB
dependency5.type=UsesDependency
dependency6.from=Window
dependency6.to=Image
dependency6.type=UsesDependency
dependency7.from=Window
dependency7.to=Quant
dependency7.type=UsesDependency
editor.fx.0.height=0
editor.fx.0.width=0
editor.fx.0.x=0
editor.fx.0.y=0
objectbench.height=100
objectbench.width=595
package.divider.horizontal=0.599601593625498
package.divider.vertical=0.8102836879432624
package.editor.height=450
package.editor.width=885
package.editor.x=0
package.editor.y=25
package.frame.height=628
package.frame.width=1024
package.numDependencies=7
package.numTargets=4
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.height=60
readme.name=@README
readme.width=48
readme.x=10
readme.y=10
target1.height=70
target1.name=Window
target1.showInterface=false
target1.type=ClassTarget
target1.width=120
target1.x=150
target1.y=280
target2.height=70
target2.name=Image
target2.showInterface=false
target2.type=ClassTarget
target2.width=120
target2.x=320
target2.y=190
target3.height=70
target3.name=RGB
target3.showInterface=false
target3.type=ClassTarget
target3.width=120
target3.x=320
target3.y=10
target4.height=70
target4.name=Quant
target4.showInterface=false
target4.type=ClassTarget
target4.width=120
target4.x=460
target4.y=100