From 43886afd0f95b90c43863f8d939246850b59560a Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Thu, 26 Apr 2018 06:35:16 -0700 Subject: More stages Added a stage to load images from files, and one to execute sub-pipelines. --- src/bjc/imgchain/ImgPicker.java | 63 +++++++++++++ src/bjc/imgchain/ImgPipeline.java | 20 ++++ src/bjc/imgchain/ImgViewer.java | 57 +++++++++++- src/bjc/imgchain/pipeline/PipelinePicker.java | 4 +- .../imgchain/pipeline/stages/BrightnessStage.java | 65 +++---------- src/bjc/imgchain/pipeline/stages/LoadStage.java | 87 +++++++++++++++++ src/bjc/imgchain/pipeline/stages/PipeStage.java | 75 +++++++++++++++ src/bjc/imgchain/pipeline/stages/StagePicker.java | 5 +- .../imgchain/pipeline/stages/ThresholdStage.java | 103 +++++++++++++++++++++ 9 files changed, 420 insertions(+), 59 deletions(-) create mode 100644 src/bjc/imgchain/ImgPicker.java create mode 100644 src/bjc/imgchain/pipeline/stages/LoadStage.java create mode 100644 src/bjc/imgchain/pipeline/stages/PipeStage.java create mode 100644 src/bjc/imgchain/pipeline/stages/ThresholdStage.java (limited to 'src/bjc') diff --git a/src/bjc/imgchain/ImgPicker.java b/src/bjc/imgchain/ImgPicker.java new file mode 100644 index 0000000..bc93089 --- /dev/null +++ b/src/bjc/imgchain/ImgPicker.java @@ -0,0 +1,63 @@ +package bjc.imgchain; + +import java.awt.BorderLayout; +import java.awt.GridLayout; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +import bjc.imgchain.ImgChain; + +public class ImgPicker extends JDialog { + private static final long serialVersionUID = 1L; + + public String imageName; + + public ImgPicker() { + super(); + + setModalityType(ModalityType.APPLICATION_MODAL); + setTitle("Recall an Image"); + + setupGUI(); + } + + private void setupGUI() { + setLayout(new BorderLayout()); + + DefaultListModel imgModel = new DefaultListModel<>(); + for (String imgName : ImgChain.chan.imageRepo.keySet()) { + imgModel.addElement(imgName); + } + + JList imgList = new JList<>(imgModel); + JScrollPane listScroll = new JScrollPane(imgList); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridLayout(2, 1)); + + JButton addStage = new JButton("Recall Image"); + addStage.addActionListener((ev) -> { + imageName = imgList.getSelectedValue(); + + setVisible(false); + dispose(); + }); + + JButton cancel = new JButton("Cancel"); + cancel.addActionListener((ev) -> { + setVisible(false); + dispose(); + }); + + buttonPanel.add(addStage); + buttonPanel.add(cancel); + + add(listScroll, BorderLayout.CENTER); + add(buttonPanel, BorderLayout.PAGE_END); + } +} diff --git a/src/bjc/imgchain/ImgPipeline.java b/src/bjc/imgchain/ImgPipeline.java index 8dbbebf..cfef3ba 100644 --- a/src/bjc/imgchain/ImgPipeline.java +++ b/src/bjc/imgchain/ImgPipeline.java @@ -20,12 +20,16 @@ import javax.swing.border.TitledBorder; import bjc.imgchain.pipeline.MutablePipeline; import bjc.imgchain.pipeline.PipelineStage; +import bjc.imgchain.pipeline.stages.BrightnessStage; import bjc.imgchain.pipeline.stages.ColorSkewStage; import bjc.imgchain.pipeline.stages.GaussStage; import bjc.imgchain.pipeline.stages.GreyscaleStage; import bjc.imgchain.pipeline.stages.IDStage; +import bjc.imgchain.pipeline.stages.LoadStage; import bjc.imgchain.pipeline.stages.NegativeStage; +import bjc.imgchain.pipeline.stages.PipeStage; import bjc.imgchain.pipeline.stages.StagePicker; +import bjc.imgchain.pipeline.stages.ThresholdStage; /** * Edit an image pipeline. @@ -148,6 +152,22 @@ public class ImgPipeline extends JInternalFrame { stag = new GaussStage(); } break; + case "Tint": { + stag = new BrightnessStage(); + } + break; + case "Threshold": { + stag = new ThresholdStage(); + } + break; + case "Sub-pipeline": { + stag = new PipeStage(); + } + break; + case "Load Image": { + stag = new LoadStage(); + } + break; default: JOptionPane.showMessageDialog(ImgChain.chan.desktop, String .format("Attempted to add unknown stage '%s'", pick.stageName)); diff --git a/src/bjc/imgchain/ImgViewer.java b/src/bjc/imgchain/ImgViewer.java index 06c2a1e..6876514 100644 --- a/src/bjc/imgchain/ImgViewer.java +++ b/src/bjc/imgchain/ImgViewer.java @@ -63,7 +63,7 @@ public class ImgViewer extends JInternalFrame { icon.setImage(ImageIO.read(tmp)); img = tmp; - + setTitle("Image Viewer - " + img.getName()); } catch (IOException e) { String msg = String.format("Error: Could not load image %s", img.getPath()); @@ -124,16 +124,44 @@ public class ImgViewer extends JInternalFrame { JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic('F'); - JMenuItem changeImage = new JMenuItem("Change Image"); - changeImage.setMnemonic('C'); + JMenuItem changeImage = new JMenuItem("Load Image from Disk"); + changeImage.setMnemonic('L'); changeImage.addActionListener(new ChangeImageListener()); JMenuItem reloadImage = new JMenuItem("Reload Image"); reloadImage.setMnemonic('R'); reloadImage.addActionListener(new ReloadImageListener(img)); - JMenuItem storeImage = new JMenuItem("Store Image"); - storeImage.setMnemonic('S'); + JMenuItem saveImage = new JMenuItem("Save Image"); + saveImage.setMnemonic('S'); + saveImage.addActionListener((ev) -> { + JFileChooser jfc = new JFileChooser(); + jfc.setMultiSelectionEnabled(false); + + int res = jfc.showSaveDialog(ImgViewer.this); + + if (res != JFileChooser.APPROVE_OPTION) { + return; + } + + try { + File tmp = jfc.getSelectedFile(); + + ImageIO.write(Utils.toBuffered(icon.getImage()), "PNG", tmp); + } catch (IOException e) { + String msg = String.format("Error: Could not save image %s", img.getPath()); + + System.out.printf("%s\n", msg); + + e.printStackTrace(); + + JOptionPane.showInternalMessageDialog(null, msg, "Error saving image", + JOptionPane.ERROR_MESSAGE); + } + }); + + JMenuItem storeImage = new JMenuItem("Stash Image to Memory"); + storeImage.setMnemonic('T'); storeImage.addActionListener((ev) -> { String inp = JOptionPane.showInternalInputDialog(this, "Enter name to store image under"); @@ -149,6 +177,25 @@ public class ImgViewer extends JInternalFrame { desktop.addImage(inp, icon.getImage()); }); + JMenuItem recallImage = new JMenuItem("Recall Image from Memory"); + recallImage.setMnemonic('F'); + recallImage.addActionListener((ev) -> { + ImgPicker pick = new ImgPicker(); + + pick.pack(); + pick.setVisible(true); + + if (pick.imageName == null) { + System.out.println("WARN: picked null image"); + return; + } + + Image imag = ImgChain.chan.imageRepo.get(pick.imageName); + + icon.setImage(imag); + lab.repaint(); + }); + fileMenu.add(changeImage); fileMenu.add(reloadImage); fileMenu.addSeparator(); diff --git a/src/bjc/imgchain/pipeline/PipelinePicker.java b/src/bjc/imgchain/pipeline/PipelinePicker.java index 92478a6..3539beb 100644 --- a/src/bjc/imgchain/pipeline/PipelinePicker.java +++ b/src/bjc/imgchain/pipeline/PipelinePicker.java @@ -21,7 +21,7 @@ public class PipelinePicker extends JDialog { super(); setModalityType(ModalityType.APPLICATION_MODAL); - setTitle("Add a Stage"); + setTitle("Apply a Pipeline"); setupGUI(); } @@ -40,7 +40,7 @@ public class PipelinePicker extends JDialog { JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new GridLayout(2, 1)); - JButton addStage = new JButton("Select Pipe"); + JButton addStage = new JButton("Apply Pipe"); addStage.addActionListener((ev) -> { pipeName = pipeList.getSelectedValue(); diff --git a/src/bjc/imgchain/pipeline/stages/BrightnessStage.java b/src/bjc/imgchain/pipeline/stages/BrightnessStage.java index 4f0feb2..ae31dcc 100644 --- a/src/bjc/imgchain/pipeline/stages/BrightnessStage.java +++ b/src/bjc/imgchain/pipeline/stages/BrightnessStage.java @@ -13,38 +13,31 @@ import bjc.imgchain.utils.LabeledInputPanel; public class BrightnessStage extends AbstractPixelStage { public BrightnessStage() { - this(1, 0, 0, 0, 1, 0, 0, 0, 1); + this(0, 0, 0); } - public BrightnessStage(int rr, int rg, int rb, int gr, int gg, int gb, - int br, int bg, int bb) { + public BrightnessStage(int rr, int gg, int bb) { super(StageType.IMGTRANS); this.rr = rr; - this.rg = rg; - this.rb = rb; - this.gr = gr; this.gg = gg; - this.gb = gb; - this.br = br; - this.bg = bg; this.bb = bb; } - private int rr, rg, rb; - private int gr, gg, gb; - private int br, bg, bb; + private int rr; + private int gg; + private int bb; @Override public int[] processPixel(int[] pix) { int[] ret = new int[4]; ret[0] = pix[0]; - ret[1] = (int) ((pix[1] + rr) + (pix[2] + rg) + (pix[3] + rb)); - ret[2] = (int) ((pix[1] + gr) + (pix[2] + gg) + (pix[3] + gb)); - ret[3] = (int) ((pix[1] + br) + (pix[2] + bg) + (pix[3] + bb)); - + ret[1] = pix[1] + rr; + ret[2] = pix[2] + gg; + ret[3] = pix[3] + bb; + ret[1] = Math.max(0, Math.min(255, ret[1])); ret[2] = Math.max(0, Math.min(255, ret[2])); ret[3] = Math.max(0, Math.min(255, ret[3])); @@ -80,53 +73,23 @@ public class BrightnessStage extends AbstractPixelStage { bSkew.setBorder(new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "Blue Balance")); - LabeledInputPanel rpercRed = new LabeledInputPanel("% Red", rr); - LabeledInputPanel rpercGreen = new LabeledInputPanel("% Green", rg); - LabeledInputPanel rpercBlue = new LabeledInputPanel("% Blue", rb); - rSkew.add(rpercRed); - rSkew.add(rpercGreen); - rSkew.add(rpercBlue); - - LabeledInputPanel gpercRed = new LabeledInputPanel("% Red", gr); - LabeledInputPanel gpercGreen = new LabeledInputPanel("% Green", gg); - LabeledInputPanel gpercBlue = new LabeledInputPanel("% Blue", gb); - gSkew.add(gpercRed); + LabeledInputPanel rpercRed = new LabeledInputPanel("+/- Red", rr); + rSkew.add(rpercRed);; + + LabeledInputPanel gpercGreen = new LabeledInputPanel("+/- Green", gg); gSkew.add(gpercGreen); - gSkew.add(gpercBlue); - LabeledInputPanel bpercRed = new LabeledInputPanel("% Red", br); - LabeledInputPanel bpercGreen = new LabeledInputPanel("% Green", bg); - LabeledInputPanel bpercBlue = new LabeledInputPanel("% Blue", bb); - bSkew.add(bpercRed); - bSkew.add(bpercGreen); + LabeledInputPanel bpercBlue = new LabeledInputPanel("+/- Blue", bb); bSkew.add(bpercBlue); rpercRed.addPropertyChangeListener("value", (ev) -> { rr = (Integer)rpercRed.field.getValue(); }); - gpercRed.addPropertyChangeListener("value", (ev) -> { - gr = (Integer)gpercRed.field.getValue(); - }); - bpercRed.addPropertyChangeListener("value", (ev) -> { - br = (Integer)bpercRed.field.getValue(); - }); - rpercGreen.addPropertyChangeListener("value", (ev) -> { - rg = (Integer)rpercGreen.field.getValue(); - }); gpercGreen.addPropertyChangeListener("value", (ev) -> { gg = (Integer)gpercGreen.field.getValue(); }); - bpercGreen.addPropertyChangeListener("value", (ev) -> { - bg = (Integer)bpercGreen.field.getValue(); - }); - rpercBlue.addPropertyChangeListener("value", (ev) -> { - rb = (Integer)rpercBlue.field.getValue(); - }); - gpercBlue.addPropertyChangeListener("value", (ev) -> { - gb = (Integer)gpercBlue.field.getValue(); - }); bpercBlue.addPropertyChangeListener("value", (ev) -> { bb = (Integer)bpercBlue.field.getValue(); }); diff --git a/src/bjc/imgchain/pipeline/stages/LoadStage.java b/src/bjc/imgchain/pipeline/stages/LoadStage.java new file mode 100644 index 0000000..ed63a89 --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/LoadStage.java @@ -0,0 +1,87 @@ +package bjc.imgchain.pipeline.stages; + +import java.awt.BorderLayout; +import java.awt.Image; +import java.io.IOException; +import java.net.URL; + +import javax.imageio.ImageIO; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import bjc.imgchain.pipeline.StageType; + +public class LoadStage extends AbstractPipelineStage { + private String fileName; + + public LoadStage() { + super(StageType.IMGSOURCE); + } + + @Override + public Image process(Image inp) { + try { + return ImageIO.read(new URL("file://" + fileName)); + } catch (IOException e) { + String msg = String.format("Error: Could not load image %s", fileName); + + System.out.printf("%s\n", msg); + + e.printStackTrace(); + + JOptionPane.showInternalMessageDialog(null, msg, "Error loading image", + JOptionPane.ERROR_MESSAGE); + } + + return inp; + } + + @Override + public String name() { + return "Load Image"; + } + + @Override + public String description() { + return "Load an image from a file"; + } + + @Override + public JComponent getEditor() { + JPanel holder = new JPanel(); + holder.setLayout(new BorderLayout()); + + JLabel fileLabel = new JLabel("File"); + + JTextField fileField = new JTextField(80); + fileField.addPropertyChangeListener("value", (ev) -> { + fileName = fileField.getText(); + }); + + JButton fileButton = new JButton("Select File"); + fileButton.addActionListener((ev) -> { + JFileChooser jfc = new JFileChooser(); + jfc.setMultiSelectionEnabled(false); + + int res = jfc.showOpenDialog(holder); + + if (res != JFileChooser.APPROVE_OPTION) { + return; + } + + fileField.setText(jfc.getSelectedFile().getAbsolutePath()); + }); + + holder.add(fileLabel, BorderLayout.LINE_START); + holder.add(fileField, BorderLayout.CENTER); + holder.add(fileButton, BorderLayout.LINE_END); + + return holder; + } + +} diff --git a/src/bjc/imgchain/pipeline/stages/PipeStage.java b/src/bjc/imgchain/pipeline/stages/PipeStage.java new file mode 100644 index 0000000..1472a3c --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/PipeStage.java @@ -0,0 +1,75 @@ +package bjc.imgchain.pipeline.stages; + +import java.awt.BorderLayout; +import java.awt.Image; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import bjc.imgchain.ImgChain; +import bjc.imgchain.pipeline.Pipeline; +import bjc.imgchain.pipeline.PipelinePicker; +import bjc.imgchain.pipeline.StageType; + +public class PipeStage extends AbstractPipelineStage { + private String pipeName; + + public PipeStage() { + super(StageType.IMGTRANS); + } + + @Override + public Image process(Image inp) { + Pipeline pipeline = ImgChain.chan.pipelineRepo.get(pipeName); + + return pipeline.process(inp); + } + + @Override + public String name() { + return "Sub-pipeline"; + } + + @Override + public String description() { + return "Run the steps of another pipeline"; + } + + @Override + public JComponent getEditor() { + JPanel holder = new JPanel(); + holder.setLayout(new BorderLayout()); + + JLabel pipeLabel = new JLabel("Pipeline"); + + JTextField pipeField = new JTextField(80); + pipeField.addPropertyChangeListener("value", (ev) -> { + pipeName = pipeField.getText(); + }); + + JButton pipeButton = new JButton("Select Pipeline"); + pipeButton.addActionListener((ev) -> { + PipelinePicker pick = new PipelinePicker(); + + pick.pack(); + pick.setVisible(true); + + if (pick.pipeName == null) { + System.out.println("WARN: picked null pipe"); + return; + } + + pipeField.setText(pick.pipeName); + }); + + holder.add(pipeLabel, BorderLayout.LINE_START); + holder.add(pipeField, BorderLayout.CENTER); + holder.add(pipeButton, BorderLayout.LINE_END); + + return holder; + } + +} diff --git a/src/bjc/imgchain/pipeline/stages/StagePicker.java b/src/bjc/imgchain/pipeline/stages/StagePicker.java index 8b30ee4..5dd9904 100644 --- a/src/bjc/imgchain/pipeline/stages/StagePicker.java +++ b/src/bjc/imgchain/pipeline/stages/StagePicker.java @@ -36,7 +36,10 @@ public class StagePicker extends JDialog { String[][] data = new String[][] { { "Identity", "Pass through image unchanged" }, { "Greyscale", "Convert a color image into greyscale" }, { "Color Skew", "Adjust color balance" }, { "Sepia", "Make your image sepia toned" }, - { "Negative", "Invert your images colors" }, { "Gaussian Blur", "Blur images" } }; + { "Negative", "Invert your images colors" }, { "Gaussian Blur", "Blur images" }, + { "Tint", "Add/remove colors" }, { "Threshold", "Convert the image to black/white" }, + { "Sub-pipeline", "Execute another pipeline" }, + { "Load Image", "Load an image from a file" } }; JTable stageTable = new JTable(new ImmutableTableModel(data, columnNames)); diff --git a/src/bjc/imgchain/pipeline/stages/ThresholdStage.java b/src/bjc/imgchain/pipeline/stages/ThresholdStage.java new file mode 100644 index 0000000..5a72946 --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/ThresholdStage.java @@ -0,0 +1,103 @@ +package bjc.imgchain.pipeline.stages; + +import java.awt.GridLayout; +import java.awt.Image; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.BevelBorder; +import javax.swing.border.TitledBorder; + +import bjc.imgchain.pipeline.StageType; +import bjc.imgchain.utils.LabeledInputPanel; + +public class ThresholdStage extends AbstractPixelStage { + public ThresholdStage() { + this(0, 0, 0); + } + + public ThresholdStage(int rr, int gg, int bb) { + super(StageType.IMGTRANS); + + this.redThreshold = rr; + this.greenThreshold = gg; + this.blueThreshold = bb; + } + + private int redThreshold; + private int greenThreshold; + private int blueThreshold; + + @Override + public int[] processPixel(int[] pix) { + int[] ret = new int[4]; + + ret[0] = pix[0]; + ret[1] = pix[1] > redThreshold ? 255 : 0; + ret[2] = pix[2] > greenThreshold ? 255 : 0; + ret[3] = pix[3] > blueThreshold ? 255 : 0; + + ret[1] = Math.max(0, Math.min(255, ret[1])); + ret[2] = Math.max(0, Math.min(255, ret[2])); + ret[3] = Math.max(0, Math.min(255, ret[3])); + + return ret; + } + + @Override + public String name() { + return "Tint"; + } + + @Override + public String description() { + return "Add/remove colors"; + } + + @Override + public JComponent getEditor() { + JPanel holder = new JPanel(); + holder.setLayout(new GridLayout(3, 1)); + + JPanel rSkew = new JPanel(); + rSkew.setLayout(new GridLayout(1, 3)); + rSkew.setBorder(new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "Red Threshold")); + + JPanel gSkew = new JPanel(); + gSkew.setLayout(new GridLayout(1, 3)); + gSkew.setBorder(new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "Green Threshold")); + + JPanel bSkew = new JPanel(); + bSkew.setLayout(new GridLayout(1, 3)); + bSkew.setBorder(new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "Blue Threshold")); + + LabeledInputPanel rpercRed = new LabeledInputPanel("Red", redThreshold); + rSkew.add(rpercRed); + ; + + LabeledInputPanel gpercGreen = new LabeledInputPanel("Green", greenThreshold); + gSkew.add(gpercGreen); + + LabeledInputPanel bpercBlue = new LabeledInputPanel("Blue", blueThreshold); + bSkew.add(bpercBlue); + + rpercRed.addPropertyChangeListener("value", (ev) -> { + redThreshold = (Integer) rpercRed.field.getValue(); + }); + + gpercGreen.addPropertyChangeListener("value", (ev) -> { + greenThreshold = (Integer) gpercGreen.field.getValue(); + }); + + bpercBlue.addPropertyChangeListener("value", (ev) -> { + blueThreshold = (Integer) bpercBlue.field.getValue(); + }); + + holder.add(rSkew); + holder.add(gSkew); + holder.add(bSkew); + + return holder; + } + +} \ No newline at end of file -- cgit v1.2.3