I've used openCV library to detect objects of specific colours. The detection of the colour can be changed by playing around with the saturation and hue. My issue is to get the x and y position of the contours that are shown in the view.
Consider the following image. I need to get the position of the yellow lined contours.
Code:
public class ObjRecognitionController {
// FXML camera button
#FXML
private Button cameraButton;
// the FXML area for showing the current frame
#FXML
private ImageView originalFrame;
// the FXML area for showing the mask
#FXML
private ImageView maskImage;
// the FXML area for showing the output of the morphological operations
#FXML
private ImageView morphImage;
// FXML slider for setting HSV ranges
#FXML
private Slider hueStart;
#FXML
private Slider hueStop;
#FXML
private Slider saturationStart;
#FXML
private Slider saturationStop;
#FXML
private Slider valueStart;
#FXML
private Slider valueStop;
// FXML label to show the current values set with the sliders
#FXML
private Label hsvCurrentValues;
// a timer for acquiring the video stream
private ScheduledExecutorService timer;
// the OpenCV object that performs the video capture
private VideoCapture capture = new VideoCapture();
// a flag to change the button behavior
private boolean cameraActive;
// property for object binding
private ObjectProperty<String> hsvValuesProp;
/**
* The action triggered by pushing the button on the GUI
*/
#FXML
private void startCamera()
{
// bind a text property with the string containing the current range of
// HSV values for object detection
hsvValuesProp = new SimpleObjectProperty<>();
this.hsvCurrentValues.textProperty().bind(hsvValuesProp);
// set a fixed width for all the image to show and preserve image ratio
this.imageViewProperties(this.originalFrame, 400);
this.imageViewProperties(this.maskImage, 200);
this.imageViewProperties(this.morphImage, 200);
if (!this.cameraActive)
{
// start the video capture
this.capture.open(0);
// is the video stream available?
if (this.capture.isOpened())
{
this.cameraActive = true;
// grab a frame every 33 ms (30 frames/sec)
Runnable frameGrabber = new Runnable() {
#Override
public void run()
{
Image imageToShow = grabFrame();
originalFrame.setImage(imageToShow);
}
};
this.timer = Executors.newSingleThreadScheduledExecutor();
this.timer.scheduleAtFixedRate(frameGrabber, 0, 33, TimeUnit.MILLISECONDS);
// update the button content
this.cameraButton.setText("Stop Camera");
}
else
{
// log the error
System.err.println("Failed to open the camera connection...");
}
}
else
{
// the camera is not active at this point
this.cameraActive = false;
// update again the button content
this.cameraButton.setText("Start Camera");
// stop the timer
try
{
this.timer.shutdown();
this.timer.awaitTermination(33, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e)
{
// log the exception
System.err.println("Exception in stopping the frame capture, trying to release the camera now... " + e);
}
// release the camera
this.capture.release();
}
}
/**
* Get a frame from the opened video stream (if any)
*
* #return the {#link Image} to show
*/
private Image grabFrame()
{
// init everything
Image imageToShow = null;
Mat frame = new Mat();
// check if the capture is open
if (this.capture.isOpened())
{
try
{
// read the current frame
this.capture.read(frame);
// if the frame is not empty, process it
if (!frame.empty())
{
// init
Mat blurredImage = new Mat();
Mat hsvImage = new Mat();
Mat mask = new Mat();
Mat morphOutput = new Mat();
// remove some noise
Imgproc.blur(frame, blurredImage, new Size(7, 7));
// convert the frame to HSV
Imgproc.cvtColor(blurredImage, hsvImage, Imgproc.COLOR_BGR2HSV);
// get thresholding values from the UI
// remember: H ranges 0-180, S and V range 0-255
Scalar minValues = new Scalar(this.hueStart.getValue(), this.saturationStart.getValue(),
this.valueStart.getValue());
Scalar maxValues = new Scalar(this.hueStop.getValue(), this.saturationStop.getValue(),
this.valueStop.getValue());
// show the current selected HSV range
String valuesToPrint = "Hue range: " + minValues.val[0] + "-" + maxValues.val[0]
+ "\tSaturation range: " + minValues.val[1] + "-" + maxValues.val[1] + "\tValue range: "
+ minValues.val[2] + "-" + maxValues.val[2];
this.onFXThread(this.hsvValuesProp, valuesToPrint);
// threshold HSV image to select tennis balls
Core.inRange(hsvImage, minValues, maxValues, mask);
// show the partial output
this.onFXThread(this.maskImage.imageProperty(), this.mat2Image(mask));
// morphological operators
// dilate with large element, erode with small ones
Mat dilateElement = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(24, 24));
Mat erodeElement = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(12, 12));
Imgproc.erode(mask, morphOutput, erodeElement);
Imgproc.erode(mask, morphOutput, erodeElement);
Imgproc.dilate(mask, morphOutput, dilateElement);
Imgproc.dilate(mask, morphOutput, dilateElement);
// show the partial output
this.onFXThread(this.morphImage.imageProperty(), this.mat2Image(morphOutput));
// find the tennis ball(s) contours and show them
frame = this.findAndDrawBalls(morphOutput, frame);
// convert the Mat object (OpenCV) to Image (JavaFX)
imageToShow = mat2Image(frame);
}
}
catch (Exception e)
{
// log the (full) error
System.err.print("ERROR");
e.printStackTrace();
}
}
return imageToShow;
}
/**
* Given a binary image containing one or more closed surfaces, use it as a
* mask to find and highlight the objects contours
*
* #param maskedImage
* the binary image to be used as a mask
* #param frame
* the original {#link Mat} image to be used for drawing the
* objects contours
* #return the {#link Mat} image with the objects contours framed
*/
private Mat findAndDrawBalls(Mat maskedImage, Mat frame) {
// init
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
// find contours
Imgproc.findContours(maskedImage, contours, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE);
// if any contour exist...
if (hierarchy.size().height > 0 && hierarchy.size().width > 0) {
// for each contour, display it in yellow
for (int idx = 0; idx >= 0; idx = (int) hierarchy.get(0, idx)[0]) {
Imgproc.drawContours(frame, contours, idx, new Scalar(0, 255, 255));
}
}
return frame;
}
/**
* Set typical {#link ImageView} properties: a fixed width and the
* information to preserve the original image ration
*
* #param image
* the {#link ImageView} to use
* #param dimension
* the width of the image to set
*/
private void imageViewProperties(ImageView image, int dimension) {
// set a fixed width for the given ImageView
image.setFitWidth(dimension);
// preserve the image ratio
image.setPreserveRatio(true);
}
/**
* Convert a {#link Mat} object (OpenCV) in the corresponding {#link Image}
* for JavaFX
*
* #param frame
* the {#link Mat} representing the current frame
* #return the {#link Image} to show
*/
private Image mat2Image(Mat frame) {
// create a temporary buffer
MatOfByte buffer = new MatOfByte();
// encode the frame in the buffer, according to the PNG format
Imgcodecs.imencode(".png", frame, buffer);
// build and return an Image created from the image encoded in the
// buffer
return new Image(new ByteArrayInputStream(buffer.toArray()));
}
/**
* Generic method for putting element running on a non-JavaFX thread on the
* JavaFX thread, to properly update the UI
*
* #param property
* a {#link ObjectProperty}
* #param value
* the value to set for the given {#link ObjectProperty}
*/
private <T> void onFXThread(final ObjectProperty<T> property, final T value)
{
Platform.runLater(new Runnable() {
#Override
public void run()
{
property.set(value);
}
});
}
}
You can get the bounded rectangle with boundingRect() function of OpenCV
Rect rect = Imgproc.boundingRect(contours.get(idx));
Now you can get the x and y positions by rect.x and rect.y
Then you can draw the rect on image mat
Imgproc.rectangle(mat, rect.tl(), rect.br(), color, THICKNESS=1 or 2 ...);
Related
I want to print pdf file that will be like the acrobat reader printing.
i tried with this code:
public static void print3() throws PrinterException, IOException {
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
if(service != null)
{
PrintService selection = ServiceUI.printDialog(null, 200, 200, services, service, null, attrs);
PrinterJob job = PrinterJob.getPrinterJob();
PDDocument document = PDDocument.load(new File("C:\\Temp\\test.pdf"));
PDFPageable pageAble = new PDFPageable(document, Orientation.AUTO);
job.setPageable(pageAble);
attrs.remove(OrientationRequested.class);
job.setPrintService(selection);
job.print(attrs);
}
}
but the text go out of the page:
if i print this pdf with acrobat reader with this options:
its working perfect.
i want to know how can i print the page like the acrobat reader with java?
its seems that the auto orientation working because its printing on landscape like he needs, but how can i scale like the acrobat to 54% automatically ?
tnx a lot
Edit:
i tried to create custom pageable by taking the source code of PDFPageable and change the scale in the getPrintable() method like this:
import java.awt.RenderingHints;
import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.printing.Orientation;
import org.apache.pdfbox.printing.Scaling;
public final class MyPageable extends Book
{
private final PDDocument document;
private final boolean showPageBorder;
private final float dpi;
private final Orientation orientation;
private boolean subsamplingAllowed = false;
private RenderingHints renderingHints = null;
/**
* Creates a new PDFPageable.
*
* #param document the document to print
*/
public MyPageable(PDDocument document)
{
this(document, Orientation.AUTO, false, 0);
}
/**
* Creates a new PDFPageable with the given page orientation.
*
* #param document the document to print
* #param orientation page orientation policy
*/
public MyPageable(PDDocument document, Orientation orientation)
{
this(document, orientation, false, 0);
}
/**
* Creates a new PDFPageable with the given page orientation and with optional page borders
* shown. The image will be rasterized at the given DPI before being sent to the printer.
*
* #param document the document to print
* #param orientation page orientation policy
* #param showPageBorder true if page borders are to be printed
*/
public MyPageable(PDDocument document, Orientation orientation, boolean showPageBorder)
{
this(document, orientation, showPageBorder, 0);
}
/**
* Creates a new PDFPageable with the given page orientation and with optional page borders
* shown. The image will be rasterized at the given DPI before being sent to the printer.
*
* #param document the document to print
* #param orientation page orientation policy
* #param showPageBorder true if page borders are to be printed
* #param dpi if non-zero then the image will be rasterized at the given DPI
*/
public MyPageable(PDDocument document, Orientation orientation, boolean showPageBorder,
float dpi)
{
this.document = document;
this.orientation = orientation;
this.showPageBorder = showPageBorder;
this.dpi = dpi;
}
/**
* Get the rendering hints.
*
* #return the rendering hints or null if none are set.
*/
public RenderingHints getRenderingHints()
{
return renderingHints;
}
/**
* Set the rendering hints. Use this to influence rendering quality and speed. If you don't set
* them yourself or pass null, PDFBox will decide <b><u>at runtime</u></b> depending on the
* destination.
*
* #param renderingHints
*/
public void setRenderingHints(RenderingHints renderingHints)
{
this.renderingHints = renderingHints;
}
/**
* Value indicating if the renderer is allowed to subsample images before drawing, according to
* image dimensions and requested scale.
*
* Subsampling may be faster and less memory-intensive in some cases, but it may also lead to
* loss of quality, especially in images with high spatial frequency.
*
* #return true if subsampling of images is allowed, false otherwise.
*/
public boolean isSubsamplingAllowed()
{
return subsamplingAllowed;
}
/**
* Sets a value instructing the renderer whether it is allowed to subsample images before
* drawing. The subsampling frequency is determined according to image size and requested scale.
*
* Subsampling may be faster and less memory-intensive in some cases, but it may also lead to
* loss of quality, especially in images with high spatial frequency.
*
* #param subsamplingAllowed The new value indicating if subsampling is allowed.
*/
public void setSubsamplingAllowed(boolean subsamplingAllowed)
{
this.subsamplingAllowed = subsamplingAllowed;
}
#Override
public int getNumberOfPages()
{
return document.getNumberOfPages();
}
/**
* {#inheritDoc}
*
* Returns the actual physical size of the pages in the PDF file. May not fit the local printer.
*/
#Override
public PageFormat getPageFormat(int pageIndex)
{
PDPage page = document.getPage(pageIndex);
PDRectangle mediaBox = MyPrintable.getRotatedMediaBox(page);
PDRectangle cropBox = MyPrintable.getRotatedCropBox(page);
// Java does not seem to understand landscape paper sizes, i.e. where width > height, it
// always crops the imageable area as if the page were in portrait. I suspect that this is
// a JDK bug but it might be by design, see PDFBOX-2922.
//
// As a workaround, we normalise all Page(s) to be portrait, then flag them as landscape in
// the PageFormat.
Paper paper;
boolean isLandscape;
if (mediaBox.getWidth() > mediaBox.getHeight())
{
// rotate
paper = new Paper();
paper.setSize(mediaBox.getHeight(), mediaBox.getWidth());
paper.setImageableArea(cropBox.getLowerLeftY(), cropBox.getLowerLeftX(),
cropBox.getHeight(), cropBox.getWidth());
isLandscape = true;
}
else
{
paper = new Paper();
paper.setSize(mediaBox.getWidth(), mediaBox.getHeight());
paper.setImageableArea(cropBox.getLowerLeftX(), cropBox.getLowerLeftY(),
cropBox.getWidth(), cropBox.getHeight());
isLandscape = false;
}
PageFormat format = new PageFormat();
format.setPaper(paper);
// auto portrait/landscape
switch (orientation)
{
case AUTO:
format.setOrientation(isLandscape ? PageFormat.LANDSCAPE : PageFormat.PORTRAIT);
break;
case LANDSCAPE:
format.setOrientation(PageFormat.LANDSCAPE);
break;
case PORTRAIT:
format.setOrientation(PageFormat.PORTRAIT);
break;
default:
break;
}
return format;
}
#Override
public Printable getPrintable(int i)
{
if (i >= getNumberOfPages())
{
throw new IndexOutOfBoundsException(i + " >= " + getNumberOfPages());
}
MyPrintable printable = new MyPrintable(document, Scaling.SHRINK_TO_FIT, showPageBorder, dpi);
printable.setSubsamplingAllowed(subsamplingAllowed);
printable.setRenderingHints(renderingHints);
return printable;
}
}
but the result is the same.
I have created a PDF with Adobe, which contains an image field called "logo"
Now if i want to add a picture using PDFBox it won't be displayed in the created pdf.
However no error message is thrown and debugging looks completely fine with a correctly created PDImageXObject object.
The used code is mostly adapted from this question:
public static void setImageField(PDDocument pdfDocument, PDAcroForm acroForm, String fieldKey, byte[] imageData)
{
final PDField retrievedField = acroForm.getField(fieldKey);
if (!(retrievedField instanceof PDPushButton)) {
throw new RuntimeException("The field: " + fieldKey + " is not of the correct type");
}
LOGGER.info("Received field: " + retrievedField.getPartialName()); // correct field is being logged
final PDPushButton imageField = (PDPushButton) retrievedField;
final List<PDAnnotationWidget> fieldWidgets = imageField.getWidgets(); // it has exactly one widget, which would be the action to set the image
if (fieldWidgets == null || fieldWidgets.size() <= 0) {
throw new RuntimeException("Misconfiguration for field: " + fieldKey + ", it has no widgets(actions)");
}
final PDAnnotationWidget imageWidget = fieldWidgets.get(0);
PDImageXObject imageForm;
try {
// This test data is resized to be smaller than the bounding box of the image element in the PDF. Just for testing purposes
final String testImage = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAARUlEQVR42u3PMREAAAgEIO2fzkRvBlcPGtCVTD3QIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIXC7VGjKHva+IvAAAAAElFTkSuQmCC";
final byte[] testData = Base64.getDecoder().decode(testImage);
imageForm = PDImageXObject.createFromByteArray(pdfDocument, testData, "logo"); // imageForm is being populated with data and no error thrown
}
catch (IOException e) {
throw new RuntimeException("Error creating Image from image data", e);
}
final float imageScaleRatio = (float) (imageForm.getHeight() / imageForm.getWidth());
final PDRectangle imagePosition = getFieldArea(imageField);
LOGGER.info("Received image position: " + imagePosition.toString());
// Retrieve the height and width and position of the rectangle where the picture will be
final float imageHeight = imagePosition.getHeight();
LOGGER.info("Image height: " + imageHeight);
final float imageWidth = imageHeight / imageScaleRatio;
LOGGER.info("Image width: " + imageWidth);
final float imageXPosition = imagePosition.getLowerLeftX();
LOGGER.info("Image X position: " + imageXPosition);
final float imageYPosition = imagePosition.getLowerLeftY();
LOGGER.info("Image Y position: " + imageYPosition);
final PDAppearanceStream documentAppearance = new PDAppearanceStream(pdfDocument);
documentAppearance.setResources(new PDResources()); // not sure why this is done
// Weird "bug" in pdfbox forces to create the contentStream always after the object to add
try (PDPageContentStream documentContentStream = new PDPageContentStream(pdfDocument, documentAppearance)) {
documentContentStream.drawImage(imageForm, imageXPosition, imageYPosition, imageWidth, imageHeight);
}
catch (IOException e) {
throw new RuntimeException("Error drawing the picture in the document", e);
}
// Setting the boundary box is mandatory for the image field! Do not remove or the flatten call on the acroform will NPE
documentAppearance.setBBox(new PDRectangle(imageXPosition, imageYPosition, imageWidth, imageHeight));
// Apply the appearance settings of the document onto the image widget ( No border )
setImageAppearance(imageWidget, documentAppearance);
// This code is normally somewhere else but for SO copied in this method to show how the pdf is being created
acroForm.flatten();
AccessPermission ap = new AccessPermission();
ap.setCanModify(false);
ap.setCanExtractContent(false);
ap.setCanFillInForm(false);
ap.setCanModifyAnnotations(false);
ap.setReadOnly();
StandardProtectionPolicy spp = new StandardProtectionPolicy("", "", ap);
spp.setEncryptionKeyLength(PdfBuildConstants.ENCRYPTION_KEY_LENTGH);
pdfDocument.protect(spp);
pdfDocument.save(pdfFile);
pdfDocument.close();
}
/**
* Applies the appearance settings of the document onto the widget to ensure a consistent look of the document.
*
* #param imageWidget The {#link PDAnnotationWidget Widget} to apply the settings to
* #param documentAppearance The {#link PDAppearanceStream Appearance settings} of the document
*/
private static void setImageAppearance(final PDAnnotationWidget imageWidget,
final PDAppearanceStream documentAppearance)
{
PDAppearanceDictionary widgetAppearance = imageWidget.getAppearance();
if (widgetAppearance == null) {
widgetAppearance = new PDAppearanceDictionary();
imageWidget.setAppearance(widgetAppearance);
}
widgetAppearance.setNormalAppearance(documentAppearance);
}
/**
* Retrieves the dimensions of the given {#link PDField Field} and creates an {#link PDRectangle Rectangle} with the
* same dimensions.
*
* #param field The {#link PDField Field} to create the rectangle for
* #return The created {#link PDRectangle Rectangle} with the dimensions of the field
*/
private static PDRectangle getFieldArea(PDField field) {
final COSDictionary fieldDictionary = field.getCOSObject();
final COSBase fieldAreaValue = fieldDictionary.getDictionaryObject(COSName.RECT);
if (!(fieldAreaValue instanceof COSArray)) {
throw new RuntimeException("The field: " + field.getMappingName() + " has no position values");
}
final COSArray fieldAreaArray = (COSArray) fieldAreaValue;
return new PDRectangle(fieldAreaArray);
}
I also looked at other questions such as this, but I can't use PDJpeg since it is not available in the current version. Additionally the original image to use can be anything from jpeg,png to gif.
I validated that the position and dimension variables being logged have the same value as the image field in the pdf file. (properties of the field)
UPDATE:
Here is an example zip containing the template pdf and the generated pdf which fills in the form fields: file upload or Dropbox
The example picture was a 50x50 png with plain green color generated from online png pixel
I'm using PDFBox 1.7.0 (I do not have a choice for the version due to old version in production server). I am trying to add an image to an existing PDF which has already a logo.
When I add the new image, the old one disappears like it is replaced.
// Use for convert mm to dots
// ... 72 dots per inch
static final int DEFAULT_USER_SPACE_UNIT_DPI = 72;
// ... mm -> inch -> dots
static final float MM_TO_UNITS = 1 / (10 * 2.54f) * DEFAULT_USER_SPACE_UNIT_DPI;
/**
* Add a given image to a specific page of a PDF
* #param document PDF document to manipulate
* #param input image inputStream
* #param pdfpage page number to target
* #param x image position (en mm)
* #param y image position (en mm)
* #param width max width of the image (mm)
* #param height max height of the image (en mm)
* #param opacity opacity level of the image (fraction)
*/
void addImageToPage (PDDocument document, InputStream input, int pdfpage, int x, int y, int width, int height, float opacity) throws IOException {
if (input != null) {
// Convert inputstream to usable BufferedImage
BufferedImage tmp_image = ImageIO.read (input);
// User TYPE_4BYTE_ABGR to fix PDFBox issue with transparent PNG
BufferedImage image = new BufferedImage (tmp_image.getWidth(), tmp_image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
// Prepare the image
image.createGraphics().drawRenderedImage (tmp_image, null);
PDXObjectImage ximage = new PDPixelMap (document, image);
// Resize the image
int iWidth = ximage.getWidth();
int iHeight = ximage.getHeight();
if (width / height > iWidth / iHeight) {
ximage.setWidth (Math.round (width * MM_TO_UNITS));
ximage.setHeight (Math.round ((iHeight * width / iWidth) * MM_TO_UNITS));
} else {
ximage.setWidth (Math.round ((iWidth * height / iHeight) * MM_TO_UNITS));
ximage.setHeight (Math.round (height * MM_TO_UNITS));
}
// Retrieve the page to update
PDPage page = (PDPage)document.getDocumentCatalog().getAllPages().get (pdfpage);
PDResources resources = page.findResources();
// Get graphics states
Map graphicsStates = resources.getGraphicsStates();
if (graphicsStates == null) {
graphicsStates = new HashMap();
}
// Set graphics states configurations
PDExtendedGraphicsState extendedGraphicsState = new PDExtendedGraphicsState();
// Set the opacity of the image
extendedGraphicsState.setNonStrokingAlphaConstant (opacity);
graphicsStates.put ("TransparentState", extendedGraphicsState);
// Restore graphics states
resources.setGraphicsStates (graphicsStates);
// Retrieve the content stream
PDPageContentStream contentStream = new PDPageContentStream (document, page, true, true);
// Activate transparency options
contentStream.appendRawCommands ("/TransparentState gs\n");
contentStream.endMarkedContentSequence();
// Insert image
contentStream.drawImage (
ximage,
(float) x * MM_TO_UNITS,
(float) y * MM_TO_UNITS
);
// close the stream
contentStream.close();
}
}
I expected to have the new image within the page, but the existing image inside the page has disappeared instead of the new one.
Example of used PDF : http://www.mediafire.com/folder/g6p7c2b5ob1c7/PDFBox_issue
There are several bugs in 1.7... one I mentioned in a comment (turns out it doesn't affect you), the other one is that the resources does some caching but isn't managed properly… long story short, you need to save and restore your xobject resources like this:
Map<String, PDXObject> xObjectsMap = page.getResources().getXObjects(); // save xobjects
…
PDXObjectImage ximage = new PDPixelMap (document, image);
String imgName = page.getResources().addXObject(ximage, "Im");
cs.drawImage(ximage, 0, 0); // bug happens here, old xobjects gets lost
xObjectsMap.put(imgName, ximage);
page.getResources().setXObjects(xObjectsMap); // restore xobjects
This is really just a workaround… there may be more bad surprises coming. You should not use old versions. They no longer spark joy. You should thank them for their service and then let them go without guilt.
Ok. I have given up trying to use PDFbox 1.7 for this part of the development. It requirement to many fixes to implements few things. It is not really maintainable for the future works. Thanks to everyone for the hints and helps.
This is how I can make a screenshot of a whole JFrame
Rectangle screenRect = cap.getBounds();
File docfile = new File("image");
BufferedImage capture = new Robot().createScreenCapture(screenRect);
ImageIO.write(capture, "png", file);
with cap being a JFrame. But when I use a JPanel for cap, I get a partly screenshot of my desktop, not only of the JPanel, what I actually want.
It may help:
/**
* Perform a screen capture of a JPanel to a Buffered Image
*
* #param panel - Panel to screen copy
* #return BufferedImage
*
* #throws AWTException - if the platform configuration does not allow low-level
* input control. This exception is always thrown when
* GraphicsEnvironment.isHeadless() returns true
*/
public static BufferedImage printScreen(JPanel panel) throws AWTException {
Point p = panel.getLocationOnScreen();
Dimension dim = panel.getSize();
Rectangle rect = new Rectangle(p, dim);
Robot robot = new Robot();
return robot.createScreenCapture(rect);
}
I currently am creating a program (with Java) that imports images and then creates a .gif with these images.
Most of my images have a transparent background with the alpha set at 0.
My current problem is after I have a series of 8 images converted to one .gif they bleed over each other. In other words the .gif does not repaint over the image before painting the next frame. So for example, if there is an apple fall from the tree, it will look like a red streak until the .gif loops.
I consider myself to be getting pretty savvy with Java but the script I found while searching the archives of the internet is way out of my league. Consider none of my 8 Java books cover IIOMETANODE. And resources on the internet are limited. So I'm not sure what the capabilities of iio are.
Here is the script:
import javax.imageio.*;
import javax.imageio.metadata.*;
import javax.imageio.stream.*;
import java.awt.image.*;
import java.io.*;
import java.util.Iterator;
public class GifCreator {
private ImageWriter gifWriter;
private ImageWriteParam imageWriteParam;
private IIOMetadata imageMetaData;
public GifCreator(){}
/**
* Creates a new GifSequenceWriter
*
* #param output the ImageOutputStream to be written to
* #param imgType one of the imageTypes specified in BufferedImage
* #param frameTime the time between frames in miliseconds
* #param loop wether the gif should loop repeatedly
* #throws IIOException if no gif ImageWriters are found
*
*/
public GifCreator(ImageOutputStream output, int imgType, int frameTime, boolean loop)
{
try {
gifWriter = getWriter();
imageWriteParam = gifWriter.getDefaultWriteParam();
ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(imgType);
imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam);
String metaFormatName = imageMetaData.getNativeMetadataFormatName();
IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName);
IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension");
graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE");
graphicsControlExtensionNode.setAttribute("delayTime", Integer.toString(frameTime / 10));
graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0");
IIOMetadataNode appEntensionsNode = getNode(root, "ApplicationExtensions");
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
child.setAttribute("applicationID", "NETSCAPE");
child.setAttribute("authenticationCode", "2.0");
int aLoop = loop ? 0 : 1;
child.setUserObject(new byte[]{ 0x1, (byte) (aLoop & 0xFF), (byte) ((aLoop >> 8) & 0xFF)});
appEntensionsNode.appendChild(child);
imageMetaData.setFromTree(metaFormatName, root);
gifWriter.setOutput(output);
gifWriter.prepareWriteSequence(null);
} catch (Exception e) {
e.printStackTrace();
}
}
private void writeToSequence(RenderedImage img) throws IOException
{
gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam);
}
/**
* Close this GifSequenceWriter object. This does not close the underlying
* stream, just finishes off the GIF.
*/
public void close() throws IOException
{
gifWriter.endWriteSequence();
}
/**
* Returns the first available GIF ImageWriter using
* ImageIO.getImageWritersBySuffix("gif").
*
* #return a GIF ImageWriter object
* #throws IIOException if no GIF image writers are returned
*/
private static ImageWriter getWriter() throws IIOException
{
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
if (!iter.hasNext()) {
throw new IIOException("No GIF Image Writers Exist");
} else {
return iter.next();
}
}
/**
* Returns an existing child node, or creates and returns a new child node (if
* the requested node does not exist).
*
* #param rootNode the <tt>IIOMetadataNode</tt> to search for the child node.
* #param nodeName the name of the child node.
*
* #return the child node, if found or a new node created with the given name.
*/
private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName)
{
int nNodes = rootNode.getLength();
for (int i = 0; i < nNodes; i++) {
if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0)
return((IIOMetadataNode) rootNode.item(i));
}
IIOMetadataNode node = new IIOMetadataNode(nodeName);
rootNode.appendChild(node);
return(node);
}
public GifCreator(BufferedImage[] imgs, String path)
{
if (imgs.length <= 1)
return;
// Grabs the first BufferedImage from the array.
BufferedImage first = imgs[0];
try {
// Creates a new BufferedOutputStream with the incoming path.
ImageOutputStream output = new FileImageOutputStream(new File(path));
// Creates a gif sequence with the type of the first image, .1 second
// between frames, which loops continuously
GifCreator writer = new GifCreator(output, first.getType(), 100, true);
// write out the first image to our sequence...
writer.writeToSequence((RenderedImage) first);
for (int i = 1; i < imgs.length; i++) {
BufferedImage next = imgs[i];
writer.writeToSequence(next);
}
writer.close();
output.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Once again I'm trying to change the .gif from bleeding images together. I attempted to create a buffer BufferedImage in between but that was a failure.
Basically, you need to change the disposalMethod...
graphicsControlExtensionNode.setAttribute("disposalMethod", "restoreToBackgroundColor");
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute(
"transparentColorFlag",
"TRUE");
Assuming that each image you are adding is a complete image and to an optimised "addition" to the image
Take a look at GIF Animation and Disposal Methods for some more details and if you're really adventurous, the GIF specification and Image :: Java Animated GIFs (with transparant pixel disposal modes) which lists the possible disposal methods, so you can have a play around and see what works...