I want to get the text which I have marked in my pdf file. I iterate over the PdfAnnotation of the PdfPage. The annoation has a method getRectangle() which return a PdfArray. I can't create from the PdfArray an Rectangle runtime-class (object/instance) which has the position and overlays over the marked text of the annotation.
With the Rectangle from annotation I wanto to filter via LocationtextExtratctionStrategy the marked content.
I written the following code to get it with iText:
package biz.hochguertel;
import com.itextpdf.kernel.color.DeviceCmyk;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.kernel.pdf.annot.PdfTextMarkupAnnotation;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.canvas.parser.EventType;
import com.itextpdf.kernel.pdf.canvas.parser.PdfCanvasProcessor;
import com.itextpdf.kernel.pdf.canvas.parser.data.IEventData;
import com.itextpdf.kernel.pdf.canvas.parser.data.TextRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.filter.TextRegionEventFilter;
import com.itextpdf.kernel.pdf.canvas.parser.listener.FilteredEventListener;
import com.itextpdf.kernel.pdf.canvas.parser.listener.LocationTextExtractionStrategy;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class AppIText {
private String filePath = getClass().getClassLoader().getResource("itext/OCA/549_OCA_Java_SE_7_Programmer_I_Certification.pdf").getFile();
private static String DEST = "demo-output/549_OCA_Java_SE_7_Programmer_I_Certification.pdf";
private PdfDocument pdfDocument;
private PdfDocument pdfWriteDoc;
public void before() throws IOException {
File file = new File(DEST);
file.getParentFile().mkdir();
if (file.exists()) {
file.delete();
}
pdfDocument = new PdfDocument(new PdfReader(filePath));
pdfWriteDoc = new PdfDocument(new PdfWriter(DEST));
}
public static void main(String[] args) throws IOException {
AppIText appIText = new AppIText();
appIText.before();
appIText.process();
appIText.close();
}
private void close() {
pdfDocument.close();
pdfWriteDoc.close();
}
private void process() {
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
PdfPage page = pdfDocument.getPage(i);
List<PdfPage> newPdfPages = pdfDocument.copyPagesTo(i, i, pdfWriteDoc);
PdfPage newPage = null;
if (newPdfPages.size() > 0) {
newPage = newPdfPages.get(0);
}
List<PdfAnnotation> annotations = page.getAnnotations();
for (PdfAnnotation annotation : annotations) {
if (annotation.getContents() != null) {
System.out.println(annotation.getContents());
if (annotation instanceof PdfTextMarkupAnnotation) {
PdfArray rectangleArray = annotation.getRectangle();
double x = ((PdfNumber) rectangleArray.get(0)).getValue();
double y = ((PdfNumber) rectangleArray.get(1)).getValue();
double xWidth = ((PdfNumber) rectangleArray.get(2)).getValue();
double yWidth = ((PdfNumber) rectangleArray.get(3)).getValue();
System.out.println(String.format("x=%s,y=%s,w=%s,h=%s", x, y, xWidth, yWidth));
Rectangle rectangle = new Rectangle((float) x, (float) y, (float) xWidth, (float) yWidth);
PdfCanvas canvas = new PdfCanvas(newPage);
canvas.setFillColor(new DeviceCmyk(1, 0, 0, 0))
.rectangle(rectangle)
.fillStroke()
;
FontFilter fontFilter = new FontFilter(rectangle);
FilteredEventListener listener = new FilteredEventListener();
LocationTextExtractionStrategy extractionStrategy = listener.attachEventListener(new LocationTextExtractionStrategy(), fontFilter);
new PdfCanvasProcessor(listener).processPageContent(page);
String actualText = extractionStrategy.getResultantText();
}
}
}
}
}
}
class RectangleEventHandler implements IEventHandler {
#Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdfDoc = docEvent.getDocument();
PdfPage page = docEvent.getPage();
PdfCanvas canvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), pdfDoc);
canvas.setFillColor(new DeviceCmyk(1, 0, 0, 0))
.rectangle(new Rectangle(20, 10, 10, 820))
.fillStroke();
}
}
class FontFilter extends TextRegionEventFilter {
public FontFilter(Rectangle filterRect) {
super(filterRect);
}
#Override
public boolean accept(IEventData data, EventType type) {
if (type.equals(EventType.RENDER_TEXT)) {
TextRenderInfo renderInfo = (TextRenderInfo) data;
PdfFont font = renderInfo.getFont();
if (null != font) {
String fontName = font.getFontProgram().getFontNames().getFontName();
return fontName.endsWith("Bold") || fontName.endsWith("Oblique");
}
}
return false;
}
}
How to create an rectangle which matches the marked area to extract only the marked (highlighted) text from the pdf?
Or is there an different way to get the marked text ov an annotation from an pdf?
The following main part of the code above is to apply:
Rectangle rectangle = new Rectangle((float) x, (float) y, (float) xWidth, (float) yWidth);
I found the solution.
The Rectancle calculation:
x: annotation.x
y: annotation.y
width: annotation.width - annotation.x
height: annotation.height - annotation.y
What I get now:
Visual Debug (if LOG_LEVEL >= 100):
Extracted Content:
13:50:01.323 [main] INFO b.h.AppIText - Annotation contents: q(7.1).explain(1)
13:50:01.323 [main] INFO b.h.AppIText - rectangleArray: x=90.0338, y=438.245, w=468.33, h=489.749
13:50:01.323 [main] INFO b.h.AppIText - pageSizeWithRotation: x=0.0, y=0.0, w=531.0, h=666.0, top=666.0, bottom=0.0, left=0.0, right=531.0
13:50:01.337 [main] INFO b.h.AppIText - str: Purpose: A finally block can’t be placed before the catch blocks. <cut here because the book is not free, but I get the complete marked text..>
My fixed code looks now:
package biz.hochguertel;
import com.itextpdf.kernel.color.DeviceCmyk;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.kernel.pdf.annot.PdfTextMarkupAnnotation;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.canvas.parser.PdfTextExtractor;
import com.itextpdf.kernel.pdf.canvas.parser.filter.TextRegionEventFilter;
import com.itextpdf.kernel.pdf.canvas.parser.listener.FilteredTextEventListener;
import com.itextpdf.kernel.pdf.canvas.parser.listener.ITextExtractionStrategy;
import com.itextpdf.kernel.pdf.canvas.parser.listener.LocationTextExtractionStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* With the help of the following documentations:
* - http://developers.itextpdf.com/content/best-itext-questions-stackoverview/content-parsing-extraction-and-redaction-text/itext7-how-read-text-specific-position
*/
public class AppIText {
private static final Logger LOGGER = LoggerFactory.getLogger(AppIText.class);
private static int LOG_LEVEL = 0;
private final static int VISUAL_DEBUG = 100;
private String filePath = getClass().getClassLoader().getResource("itext/OCA/393-394,549-550_OCA_Java_SE_7_Programmer_I_Certification.pdf").getFile();
private static String DEST = "demo-output/393-394,549-550_OCA_Java_SE_7_Programmer_I_Certification.pdf";
private PdfDocument pdfDocument;
private PdfDocument pdfWriteDoc;
public void before() throws IOException {
File file = new File(DEST);
file.getParentFile().mkdir();
if (file.exists()) {
file.delete();
}
pdfDocument = new PdfDocument(new PdfReader(filePath));
pdfWriteDoc = new PdfDocument(new PdfWriter(DEST));
}
public static void main(String[] args) throws IOException {
AppIText appIText = new AppIText();
appIText.before();
appIText.process();
}
private void process() {
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
PdfPage page = pdfDocument.getPage(i);
List<PdfPage> newPdfPages = pdfDocument.copyPagesTo(i, i, pdfWriteDoc);
PdfPage newPage = null;
if (newPdfPages.size() > 0) {
newPage = newPdfPages.get(0);
}
List<PdfAnnotation> annotations = page.getAnnotations();
for (PdfAnnotation annotation : annotations) {
if (annotation.getContents() != null) {
System.out.println();
LOGGER.info("Annotation contents: {}", annotation.getContents());
if (annotation instanceof PdfTextMarkupAnnotation) {
PdfArray rectangleArray = annotation.getRectangle();
LOGGER.info("rectangleArray: x={}, y={}, w={}, h={}",
rectangleArray.get(0),
rectangleArray.get(1),
rectangleArray.get(2),
rectangleArray.get(3)
);
Rectangle pageSizeWithRotation = page.getCropBox();
LOGGER.info("pageSizeWithRotation: x={}, y={}, w={}, h={}, top={}, bottom={}, left={}, right={}",
pageSizeWithRotation.getX(),
pageSizeWithRotation.getY(),
pageSizeWithRotation.getWidth(),
pageSizeWithRotation.getHeight(),
pageSizeWithRotation.getTop(),
pageSizeWithRotation.getBottom(),
pageSizeWithRotation.getLeft(),
pageSizeWithRotation.getRight()
);
float x = ((PdfNumber) rectangleArray.get(0)).floatValue();
float y = ((PdfNumber) rectangleArray.get(1)).floatValue();
float width = ((PdfNumber) rectangleArray.get(2)).floatValue() - x;
float height = ((PdfNumber) rectangleArray.get(3)).floatValue() - y;
Rectangle rectangle = new Rectangle(
x,
y,
width,
height
);
//13:10:33.097 [main] INFO b.h.AppIText - Annotation contents: q(7.1).explain(1)
//13:10:33.107 [main] INFO b.h.AppIText - rectangleArray: x=90.0338, y=438.245, w=468.33, h=489.749
//13:10:33.107 [main] INFO b.h.AppIText - pageSizeWithRotation: x=0.0, y=0.0, w=531.0, h=666.0, top=666.0, bottom=0.0, left=0.0, right=531.0
//width: 468.33f - 90.0388f,
//height: 489.749f - 438.245f
if (LOG_LEVEL >= VISUAL_DEBUG) {
PdfCanvas canvas = new PdfCanvas(newPage);
canvas.setFillColor(new DeviceCmyk(1, 0, 0, 0))
.rectangle(rectangle)
.fillStroke();
}
TextRegionEventFilter regionFilter = new TextRegionEventFilter(rectangle);
ITextExtractionStrategy strategy = new FilteredTextEventListener(new LocationTextExtractionStrategy(), regionFilter);
String str = PdfTextExtractor.getTextFromPage(page, strategy) + "\n";
LOGGER.info("str: {}", str);
}
}
}
}
pdfDocument.close();
pdfWriteDoc.close();
}
}
Related
I am developing a program that will add annotations over specific words that are found in a document. The annotations appear to be the right size, the X coordinates are pretty close (not perfect) but the Y coordinate is shifted considerably. My assumption here is that the coordinate systems are different but it's not clear how to adjust the Y coordinates to place it correctly.
Here is the code, the most important method is placeAnnotations()
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationTextMarkup;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class Sample {
private static final float FROM_72_TO_300_DPI = 1;
public static void main(String[] args) throws FileNotFoundException, IOException {
PDDocument doc = PDDocument.load(new File("test.pdf"));
try {
//insert new page
PDPage page = (PDPage) doc.getDocumentCatalog().getPages().get(0);
List<WordBBox> words = getWords(doc, 0);
placeAnnotations(words, page);
doc.save("test-out.pdf");
} finally {
doc.close();
}
}
public static List<WordBBox> getWords(PDDocument document, int page) throws IOException {
CustomPDFTextStripper customPDFTextStripper = new CustomPDFTextStripper();
customPDFTextStripper.setSortByPosition(true);
customPDFTextStripper.setStartPage(page);
customPDFTextStripper.setEndPage(page + 1);
Writer writer = new OutputStreamWriter(new ByteArrayOutputStream());
customPDFTextStripper.writeText(document, writer);
List<WordBBox> words = customPDFTextStripper.getWords();
return words;
}
private static void placeAnnotations(List<WordBBox> words, PDPage page) throws IOException {
List<PDAnnotation> annotations = page.getAnnotations();
for (WordBBox word : words) {
if (word.word.toLowerCase().equals("simple") || word.word.toLowerCase().equals("pdf")) {
//generate instanse for annotation
PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT);
//set the rectangle
PDRectangle position = new PDRectangle();
position.setLowerLeftX(word.x);
position.setLowerLeftY(word.y + word.height);
position.setUpperRightX(word.x + word.width);
position.setUpperRightY(word.y);
txtMark.setRectangle(position);
//set the quadpoint
float[] quads = new float[8];
//x1,y1
quads[0] = position.getLowerLeftX();
quads[1] = position.getUpperRightY();
//x2,y2
quads[2] = position.getUpperRightX();
quads[3] = quads[1];
//x3,y3
quads[4] = quads[0];
quads[5] = position.getLowerLeftY();
//x4,y4
quads[6] = quads[2];
quads[7] = quads[5];
txtMark.setRectangle(position);
txtMark.setQuadPoints(quads);
txtMark.setAnnotationName("My annotation - " + word.word);
txtMark.setTitlePopup("For '" + word.word + "'");
txtMark.setContents("Highlighted since it's important for word '" + word.word + "'");
txtMark.setRichContents("Here is some rich content for the word '" + word.word + "'");
PDColor blue = new PDColor(new float[]{0, 0, 1}, PDDeviceRGB.INSTANCE);
txtMark.setColor(blue);
txtMark.setInvisible(false);
txtMark.setNoView(false);
txtMark.setNoZoom(false);
txtMark.setLocked(false);
txtMark.setHidden(false);
txtMark.constructAppearances();
annotations.add(txtMark);
}
}
page.setAnnotations(annotations);
}
private static class CustomPDFTextStripper extends PDFTextStripper {
private final List<WordBBox> words;
public CustomPDFTextStripper() throws IOException {
this.words = new ArrayList<>();
}
public List<WordBBox> getWords() {
return new ArrayList<>(words);
}
#Override
protected void writeString(String text, List<TextPosition> textPositions) throws IOException {
String wordSeparator = getWordSeparator();
List<TextPosition> wordTextPositions = new ArrayList<>();
for (TextPosition textPosition : textPositions) {
String str = textPosition.getUnicode();
if (wordSeparator.equals(str)) {
if (!wordTextPositions.isEmpty()) {
this.words.add(createWord(wordTextPositions));
wordTextPositions.clear();
}
} else {
wordTextPositions.add(textPosition);
}
}
super.writeString(text, textPositions);
}
private WordBBox createWord(List<TextPosition> wordTextPositions) {
String word = wordTextPositions.stream()
.map(TextPosition::getUnicode)
.collect(Collectors.joining());
int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE;
int maxY = Integer.MIN_VALUE;
for (TextPosition wordTextPosition : wordTextPositions) {
minX = Math.min(minX, from72To300Dpi(wordTextPosition.getXDirAdj()));
minY = Math.min(minY, from72To300Dpi(wordTextPosition.getYDirAdj() - wordTextPosition.getHeightDir()));
maxX = Math.max(maxX, from72To300Dpi(wordTextPosition.getXDirAdj() + wordTextPosition.getWidthDirAdj()));
maxY = Math.max(maxY, from72To300Dpi(wordTextPosition.getYDirAdj()));
}
return new WordBBox(word, minX, minY, maxX - minX, maxY - minY);
}
}
private static int from72To300Dpi(float f) {
return Math.round(f * FROM_72_TO_300_DPI);
}
private static class WordBBox {
private String word = null;
private int x = 0;
private int y = 0;
private int width = 0;
private int height = 0;
public WordBBox(String word, int x, int y, int width, int height) {
this.word = word;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
}
The PDF I am using to test is here: http://www.africau.edu/images/default/sample.pdf
As you can see in this screenshot the annotations are at the bottom of the page, when they should be placed over the words 'Simple' and 'PDF'
I am running the following code so that I can create combined PDF files. And the code works with some PDF's, for example if I want to combine 10 together. But when I try to combine all of the PDF's, such as 50, I get the following messages in my log, and the PDF file is not created.
org.apache.pdfbox.cos.COSDocument finalize
WARNING: Warning: You did not close a PDF Document
The Code:
proc groovy;
add classpath = "K:/qa/common/java/groovy-json-2.5.6.jar";
add classpath = "K:/qa/common/java/pdfbox-app-2.0.15.jar";
submit;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageFitWidthDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.awt.Color;
public class ListFiles
{
public static PDDocument doc;
public static PDDocumentOutline outline;
public static PDOutlineItem pagesOutline;
public static PDPage toc;
public static PDPageXYZDestination tocMain;
public static PDActionGoTo action;
public static PDAnnotationLink link;
public static PDRectangle rect;
public static PDPageDestination pdest;
public static PDPageContentStream stream;
public static PDFont font = PDType1Font.HELVETICA_BOLD;
private static String tlfloc = "K:/qa/for_pdf/NSCLC/";
private static double X_TOC_POS = 85;
private static double Y_TOC_POS = 720;
private static double INCREM = -35;
private static float LOWER_LEFT_X = 72.0f;
private static float LOWER_LEFT_Y = 700.0f;
private static float UPPER_RIGHT_X = 610.0f;
private static float UPPER_RIGHT_Y = 730.0f;
private static float RECT_INCREM = -35.0f;
private static float TOC_ROW_FONT_SIZE = 6.0f;
public static ListFiles listFiles;
public static void main(String[] args)
{
doc = new PDDocument();
toc = new PDPage();
doc.addPage(toc);
PDDocumentOutline outline = new PDDocumentOutline();
doc.getDocumentCatalog().setDocumentOutline(outline);
pagesOutline = new PDOutlineItem();
pagesOutline.setTitle("All TFLs");
outline.addLast(pagesOutline);
File file = new File("K:/qa/for_pdf/NSCLC/NSCLC_Titles.csv");
listFiles = new ListFiles();
listFiles.listAllFiles(file);
pagesOutline.openNode();
outline.openNode();
tocMain = new PDPageXYZDestination();
tocMain.setPage(toc);
tocMain.setLeft(0);
tocMain.setTop(0); // link to top of page, this is the XYZ part
doc.save(new File("K:/qa/for_pdf/NSCLC/Combined_PDF_NSCLC3.pdf"));
doc.close();
}
// Uses listFiles method
public void tocLink(PDPage thePage, String theRowText, double theIncrem , float llx, float lly, float urx, float ury) throws IOException
{
try
{
PDPageContentStream stream = new PDPageContentStream(doc, toc , true, true);
action = new PDActionGoTo();
action.setDestination(tocMain);
// add the GoTo action
pdest = new PDPageFitWidthDestination();
pdest.setPage(thePage);
action.setDestination(pdest);
link = new PDAnnotationLink();
link.setAction(action);
stream.setNonStrokingColor(Color.white);
rect = new PDRectangle();
rect.setLowerLeftX(llx);
rect.setLowerLeftY(lly);
rect.setUpperRightX(urx);
rect.setUpperRightY(ury);
link.setRectangle(rect);
toc.getAnnotations().add(link);
stream.setNonStrokingColor(Color.black);
stream.beginText();
stream.setFont(font, TOC_ROW_FONT_SIZE);
stream.setTextTranslation(X_TOC_POS, Y_TOC_POS + theIncrem);
stream.drawString(theRowText);
stream.endText();
stream.close();
}
catch (Exception e)
{ // TODO Auto-generated catch block
e.printStackTrace();
}
}
// Uses listFiles method
public void listAllFiles(File theFile)
{
BufferedReader br = new BufferedReader(new FileReader(theFile));
String st;
int tlgCount = 1;
st = br.readLine();
while ((st = br.readLine()) != null)
{
String[] array2 = st.split(",",2);
File pdf = new File(tlfloc + array2[0] + ".pdf");
readContent(pdf, array2[1], tlgCount);
tlgCount ++;
}
}
public void readContent(File file, String theTLGTitle, int tlgIndex) throws IOException
{
try
{ File file2 = new File(file.getCanonicalPath());
PDDocument tempDoc = PDDocument.load(file2);
int numOfPages = tempDoc.getNumberOfPages();
// Stores the first page of TLG. This will be used as the bookmark destination.
PDPage page = (PDPage) tempDoc.getDocumentCatalog().getPages().get(0);
for(int i = 0; i < numOfPages; i++)
{ PDPage firstPage = (PDPage) tempDoc.getDocumentCatalog().getPages().get(i);
doc.addPage(firstPage);
// Set the bookmark title(file name) to reference first page of TFL.
if (i == 0)
{
PDPageDestination dest = new PDPageFitWidthDestination();
dest.setPage(page);
PDOutlineItem bookmark = new PDOutlineItem();
bookmark.setDestination(dest);
String title = theTLGTitle;
bookmark.setTitle(title);
pagesOutline.addLast(bookmark);
listFiles.tocLink(firstPage, theTLGTitle, INCREM * (tlgIndex), (float) LOWER_LEFT_X ,(float) LOWER_LEFT_Y + (RECT_INCREM * (tlgIndex)),
(float) UPPER_RIGHT_X ,(float) UPPER_RIGHT_Y + (RECT_INCREM * (tlgIndex)));
}
}
}
catch (Exception e)
{ // TODO Auto-generated catch block
e.printStackTrace();
}
}
}
endsubmit;
quit;
Thank you in advance.
I have a VerticalTextPainter for my column headers.
I would like to set the column header height to the maximum of all column heights.
If I set setCalculateByTextHeight(true) and setCalculateByTextLength(true) it will resize the columns as I scroll to the maximum of what is currently displayed. Instead I would like it to resize to the maximum of all columns and allow the user to change the widths/heights after.
Is it possible to get the maximum height of all column headers?
Update
I've tried removing calculating using text height/lengths and adding InitializeAutoResizeColumnsCommand. This makes the column header height be very small and only show "...".
this is the NatTable.
for (int i = 0; i < getColumnCount(); i++) {
InitializeAutoResizeColumnsCommand columnCommand = new InitializeAutoResizeColumnsCommand(this, i,
getConfigRegistry(), new GCFactory(this));
doCommand(columnCommand);
}
for (int i = 0; i < getRowCount(); i++) {
InitializeAutoResizeRowsCommand rowCommand = new InitializeAutoResizeRowsCommand(this, i,
getConfigRegistry(), new GCFactory(this));
doCommand(rowCommand);
}
Full Example (With a composite layer for columns)
I've created an example with a VerticalTextPainter for the column headers. I added a listener to resize the columns when the table is first painted.
import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
import org.eclipse.nebula.widgets.nattable.data.convert.DefaultDisplayConverter;
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.data.DummyBodyDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;
import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;
import org.eclipse.nebula.widgets.nattable.layer.CompositeLayer;
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
import org.eclipse.nebula.widgets.nattable.layer.cell.IConfigLabelAccumulator;
import org.eclipse.nebula.widgets.nattable.painter.cell.CellPainterWrapper;
import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;
import org.eclipse.nebula.widgets.nattable.painter.cell.TextPainter;
import org.eclipse.nebula.widgets.nattable.painter.cell.VerticalTextPainter;
import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.CustomLineBorderDecorator;
import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.LineBorderDecorator;
import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.PaddingDecorator;
import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer;
import org.eclipse.nebula.widgets.nattable.resize.MaxCellBoundsHelper;
import org.eclipse.nebula.widgets.nattable.resize.command.MultiRowResizeCommand;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
import org.eclipse.nebula.widgets.nattable.style.BorderStyle;
import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import org.eclipse.nebula.widgets.nattable.style.HorizontalAlignmentEnum;
import org.eclipse.nebula.widgets.nattable.style.Style;
import org.eclipse.nebula.widgets.nattable.style.VerticalAlignmentEnum;
import org.eclipse.nebula.widgets.nattable.util.GCFactory;
import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
public class ExampleNatTable {
private BodyLayerStack bodyLayer;
private int statusColumn;
private int statusRejected;
private int statusInProgress;
private boolean check = false;
private NatTable nattable;
private String[] summaryProperties;
private String[] properties;
private static final String FOO_LABEL = "FOO";
private static final String CELL_LABEL = "Cell_LABEL";
public static void main(String[] args) {
new ExampleNatTable();
}
public ExampleNatTable() {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
summaryProperties = new String[3];
for (int i = 0; i < summaryProperties.length; i++) {
summaryProperties[i] = "s" + i;
}
properties = new String[3];
for (int i = 0; i < properties.length; i++) {
properties[i] = "Column" + i;
}
// Setting the data layout layer
GridData gridData = new GridData();
gridData.heightHint = 1;
gridData.widthHint = 1;
IConfigRegistry configRegistry = new ConfigRegistry();
// Body Data Provider
IDataProvider dataProvider = new DataProvider(properties.length, 55);
bodyLayer = new BodyLayerStack(dataProvider);
// datalayer.addConfiguration(new
// Column Data Provider
DefaultColumnHeaderDataProvider columnSummaryData = new DefaultColumnHeaderDataProvider(summaryProperties);
DefaultColumnHeaderDataProvider columnData = new DefaultColumnHeaderDataProvider(properties);
ColumnHeaderLayerStack columnSummaryLayer = new ColumnHeaderLayerStack(columnSummaryData);
ColumnHeaderLayerStack columnlayer = new ColumnHeaderLayerStack(columnData);
/**
* Composite layer
*/
final CompositeLayer columnCompositeLayer = new CompositeLayer(1, 2);
columnCompositeLayer.setChildLayer("SUMMARY_REGION", columnSummaryLayer, 0, 0);
columnCompositeLayer.setChildLayer("COLUMNS", columnlayer, 0, 1);
// Row Data Provider
DefaultRowHeaderDataProvider rowdata = new DefaultRowHeaderDataProvider(dataProvider);
RowHeaderLayerStack rowlayer = new RowHeaderLayerStack(rowdata);
// Corner Data Provider
DefaultCornerDataProvider cornerdata = new DefaultCornerDataProvider(columnData, rowdata);
DataLayer cornerDataLayer = new DataLayer(cornerdata);
CornerLayer cornerLayer = new CornerLayer(cornerDataLayer, rowlayer, columnCompositeLayer);
GridLayer gridlayer = new GridLayer(bodyLayer, columnCompositeLayer, rowlayer, cornerLayer);
nattable = new NatTable(shell, gridlayer, false);
// Change for paint
IConfigLabelAccumulator cellLabelAccumulator = new IConfigLabelAccumulator() {
// #Override
#Override
public void accumulateConfigLabels(LabelStack configLabels, int columnPosition, int rowPosition) {
int columnIndex = bodyLayer.getColumnIndexByPosition(columnPosition);
int rowIndex = bodyLayer.getRowIndexByPosition(rowPosition);
if (columnIndex == 2 && rowIndex == 45) {
configLabels.addLabel(FOO_LABEL);
} else if ((columnIndex == statusColumn) && (rowIndex == statusRejected) && (check == true)) {
configLabels.addLabel(CELL_LABEL);
}
}
};
bodyLayer.setConfigLabelAccumulator(cellLabelAccumulator);
// nattable.addConfiguration(new DefaultNatTableStyleConfiguration());
nattable.addConfiguration(new AbstractRegistryConfiguration() {
// #Override
#Override
public void configureRegistry(IConfigRegistry configRegistry) {
/**
* Column Header
*/
final Style columnHeaderStyle = new Style();
columnHeaderStyle.setAttributeValue(CellStyleAttributes.VERTICAL_ALIGNMENT,
VerticalAlignmentEnum.BOTTOM);
columnHeaderStyle.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT,
HorizontalAlignmentEnum.CENTER);
configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, columnHeaderStyle,
DisplayMode.NORMAL, GridRegion.COLUMN_HEADER);
final VerticalTextPainter columnHeaderPainter = new VerticalTextPainter(false, true, false);
Display display = Display.getCurrent();
Color blue = display.getSystemColor(SWT.COLOR_BLUE);
final CellPainterWrapper columnHeaderDecorator = new CustomLineBorderDecorator(
new PaddingDecorator(columnHeaderPainter, 3, 0, 3, 0),
new BorderStyle(1, blue, BorderStyle.LineStyleEnum.SOLID));
configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, columnHeaderDecorator,
DisplayMode.NORMAL, GridRegion.COLUMN_HEADER);
/**
* Cells
*/
final Color bgColor = GUIHelper.COLOR_WHITE;
final Color fgColor = GUIHelper.COLOR_BLACK;
final Color gradientBgColor = GUIHelper.COLOR_WHITE;
final Color gradientFgColor = GUIHelper.getColor(136, 212, 215);
final Font font = GUIHelper.DEFAULT_FONT;
final HorizontalAlignmentEnum hAlign = HorizontalAlignmentEnum.CENTER;
final VerticalAlignmentEnum vAlign = VerticalAlignmentEnum.MIDDLE;
final BorderStyle borderStyle = null;
final ICellPainter cellPainter = new LineBorderDecorator(new TextPainter(false, true, 5, true));
configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, cellPainter);
Style cellStyle = new Style();
cellStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, bgColor);
cellStyle.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR, fgColor);
cellStyle.setAttributeValue(CellStyleAttributes.GRADIENT_BACKGROUND_COLOR, gradientBgColor);
cellStyle.setAttributeValue(CellStyleAttributes.GRADIENT_FOREGROUND_COLOR, gradientFgColor);
cellStyle.setAttributeValue(CellStyleAttributes.FONT, font);
cellStyle.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT, hAlign);
cellStyle.setAttributeValue(CellStyleAttributes.VERTICAL_ALIGNMENT, vAlign);
cellStyle.setAttributeValue(CellStyleAttributes.BORDER_STYLE, borderStyle);
configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, cellStyle);
configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER,
new DefaultDisplayConverter());
}
});
nattable.setLayoutData(gridData);
nattable.setConfigRegistry(configRegistry);
nattable.configure();
nattable.addListener(SWT.Paint, new Listener() {
boolean resized = false;
#Override
public void handleEvent(Event arg0) {
if (resized) {
return;
}
resized = true;
int[] gridRowHeights = MaxCellBoundsHelper.getPreferredRowHeights(nattable.getConfigRegistry(),
new GCFactory(nattable), nattable, new int[] {
0
});
if (gridRowHeights != null) {
nattable.doCommand(new MultiRowResizeCommand(nattable, new int[] {
0
}, gridRowHeights));
nattable.doCommand(new MultiRowResizeCommand(columnlayer, new int[] {
0
}, gridRowHeights));
}
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
public class DataProvider extends DummyBodyDataProvider {
public DataProvider(int columnCount, int rowCount) {
super(columnCount, rowCount);
}
#Override
public int getColumnCount() {
return properties.length;
}
#Override
public Object getDataValue(int columnIndex, int rowIndex) {
return new String("" + columnIndex + ":" + rowIndex);
}
#Override
public int getRowCount() {
return 55;
}
#Override
public void setDataValue(int arg0, int arg1, Object arg2) {
}
}
public class BodyLayerStack extends AbstractLayerTransform {
private SelectionLayer selectionLayer;
public BodyLayerStack(IDataProvider dataProvider) {
DataLayer bodyDataLayer = new DataLayer(dataProvider);
ColumnReorderLayer columnReorderLayer = new ColumnReorderLayer(bodyDataLayer);
ColumnHideShowLayer columnHideShowLayer = new ColumnHideShowLayer(columnReorderLayer);
this.selectionLayer = new SelectionLayer(columnHideShowLayer);
ViewportLayer viewportLayer = new ViewportLayer(this.selectionLayer);
setUnderlyingLayer(viewportLayer);
}
public SelectionLayer getSelectionLayer() {
return this.selectionLayer;
}
}
public class ColumnHeaderLayerStack extends AbstractLayerTransform {
public ColumnHeaderLayerStack(IDataProvider dataProvider) {
DataLayer dataLayer = new DataLayer(dataProvider);
ColumnHeaderLayer colHeaderLayer = new ColumnHeaderLayer(dataLayer, ExampleNatTable.this.bodyLayer,
ExampleNatTable.this.bodyLayer.getSelectionLayer());
setUnderlyingLayer(colHeaderLayer);
}
}
public class RowHeaderLayerStack extends AbstractLayerTransform {
public RowHeaderLayerStack(IDataProvider dataProvider) {
DataLayer dataLayer = new DataLayer(dataProvider, 50, 20);
RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(dataLayer, ExampleNatTable.this.bodyLayer,
ExampleNatTable.this.bodyLayer.getSelectionLayer());
setUnderlyingLayer(rowHeaderLayer);
}
}
}
It sounds like you need an initial auto resize instead of the automatic size configuration.
Have a look at our FAQ section how you can trigger an auto resize. Then of course you need to disable the painter calculate mechanism.
https://www.eclipse.org/nattable/documentation.php?page=faq
For the column header the described mechanism does not work, as the InitializeAutoResizeRowsCommandHandler is registered on the SelectionLayer and for the column header there is no SelectionLayer.
With the 1.6 API you could simply execute this to auto resize the column header row:
this.nattable.doCommand(new AutoResizeRowsCommand(this.nattable, 0));
With an older API you will need to copy the implementation code, as there is no way to avoid the position transformation, and therefore things won't work:
int[] gridRowHeights = MaxCellBoundsHelper.getPreferredRowHeights(
this.nattable.getConfigRegistry(),
new GCFactory(this.nattable),
this.nattable,
new int[] { 0 });
if (gridRowHeights != null) {
this.nattable.doCommand(
new MultiRowResizeCommand(this.nattable, new int[] { 0 }, gridRowHeights, true));
}
In a composition the solution for versions < 1.6 the resize command needs to be calculated and triggered for each row, as the command needs to be transported to the lowest layer in the stack. For the above example it should look like this:
int[] gridRowHeights1 = MaxCellBoundsHelper.getPreferredRowHeights(
ExampleNatTable.this.nattable.getConfigRegistry(),
new GCFactory(ExampleNatTable.this.nattable),
ExampleNatTable.this.nattable,
new int[] { 0 });
int[] gridRowHeights2 = MaxCellBoundsHelper.getPreferredRowHeights(
ExampleNatTable.this.nattable.getConfigRegistry(),
new GCFactory(ExampleNatTable.this.nattable),
ExampleNatTable.this.nattable,
new int[] { 1 });
if (gridRowHeights1 != null) {
ExampleNatTable.this.nattable.doCommand(
new MultiRowResizeCommand(
ExampleNatTable.this.nattable,
new int[] { 0 },
gridRowHeights1));
}
if (gridRowHeights2 != null) {
ExampleNatTable.this.nattable.doCommand(
new MultiRowResizeCommand(
ExampleNatTable.this.nattable,
new int[] { 1 },
gridRowHeights2));
}
The following is my code to load a tensorflow model in java for object detection and execute the same in storm:
package object_det.object_det;
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import org.apache.storm.LocalCluster;
import org.apache.storm.kafka.BrokerHosts;
import org.apache.storm.kafka.KafkaSpout;
import org.apache.storm.kafka.SpoutConfig;
import org.apache.storm.kafka.StringScheme;
import org.apache.storm.kafka.ZkHosts;
import org.apache.storm.spout.SchemeAsMultiScheme;
import org.apache.storm.topology.BasicOutputCollector;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.TopologyBuilder;
import org.apache.storm.topology.base.BaseBasicBolt;
import org.apache.storm.tuple.Tuple;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;
import org.tensorflow.SavedModelBundle;
import org.tensorflow.Tensor;
import org.tensorflow.framework.ConfigProto;
import org.tensorflow.framework.GPUOptions;
import org.tensorflow.framework.MetaGraphDef;
import org.tensorflow.framework.SignatureDef;
import org.tensorflow.framework.TensorInfo;
import org.tensorflow.types.UInt8;
import com.google.protobuf.TextFormat;
import object_detection.protos.StringIntLabelMapOuterClass.StringIntLabelMap;
import object_detection.protos.StringIntLabelMapOuterClass.StringIntLabelMapItem;
public class Objectdetect1 {
static int left = 0;
static int bot = 0;
static int top = 0;
static int right = 0;
public static class PrinterBolt extends BaseBasicBolt {
int ii = 1;
JFrame frame=new JFrame();
JLabel jLabel = new JLabel();
static {
nu.pattern.OpenCV.loadShared();
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
}
public void execute(Tuple tuple, BasicOutputCollector collector) {
String output = tuple.getString(0);
output = output.replaceAll("\\[", "").replaceAll("\\]","");
JSONParser parser = new JSONParser();
JSONObject json = null;
try {
json = (JSONObject) parser.parse(output);
} catch (ParseException e) {
e.printStackTrace();
}
long rows = (Long) json.get("rows");
int row=(int)rows;
long columns = (Long) json.get("cols");
int cols=(int)columns;
long type_data = (Long) json.get("type");
int typedata=(int)type_data;
String base64 = (String) json.get("data");
String cameraId = (String) json.get("cameraId");
String timestamp = (String) json.get("timestamp");
Mat mat1 = new Mat(row,cols, typedata);
mat1.put(0, 0, Base64.getDecoder().decode(base64));
String[] labels = null;
try {
labels = loadLabels("label.pbtxt");
} catch (Exception e) {
e.printStackTrace();
}
try (SavedModelBundle model = SavedModelBundle.load("model", "serve")) {
printSignature(model);
List<Tensor<?>> outputs = null;
try (Tensor<UInt8> input = makeImageTensor(mat1))
{
outputs =
model
.session()
.runner()
.feed("image_tensor", input)
.fetch("detection_scores")
.fetch("detection_classes")
.fetch("detection_boxes")
.run();
}
try (Tensor<Float> scoresT = outputs.get(0).expect(Float.class);
Tensor<Float> classesT = outputs.get(1).expect(Float.class);
Tensor<Float> boxesT = outputs.get(2).expect(Float.class)) {
int maxObjects = (int) scoresT.shape()[1];
float[] scores = scoresT.copyTo(new float[1][maxObjects])[0];
float[] classes = classesT.copyTo(new float[1][maxObjects])[0];
float[][] boxes = boxesT.copyTo(new float[1][maxObjects][4])[0];
boolean foundSomething = false;
int cnt = 0;
for (int i = 0; i < scores.length; ++i) {
if (scores[i] < 0.5) {
continue;
}
cnt ++;
foundSomething = true;
System.out.printf("\tFound %-20s (score: %.4f)\n", labels[(int) classes[i]], scores[i]);
left = (int) Math.round(boxes[i][1] * cols);
top = (int) Math.round(boxes[i][0] * row);
right = (int) Math.round(boxes[i][3] * cols);
bot = (int) Math.round(boxes[i][2] * row);
Imgproc.rectangle(mat1, new Point(right,bot), new Point(left,top),new Scalar(0,69,255),2);
Imgproc.putText(mat1,labels[(int) classes[i]] , new Point(left,top), Core.FONT_HERSHEY_PLAIN, 1.6, new Scalar(240,248,255),2);
BufferedImage bimg = bufferedImage(mat1);
ImageIcon imageIcon = new ImageIcon(bimg);
jLabel.setIcon(imageIcon);
frame.getContentPane().add(jLabel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
if (!foundSomething) {
System.out.println("No objects detected with a high enough score.");
BufferedImage bimg = bufferedImage(mat1);
ImageIcon imageIcon = new ImageIcon(bimg);
jLabel.setIcon(imageIcon);
frame.getContentPane().add(jLabel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
private static void printSignature(SavedModelBundle model) throws Exception {
MetaGraphDef m = MetaGraphDef.parseFrom(model.metaGraphDef());
SignatureDef sig = m.getSignatureDefOrThrow("serving_default");
int numInputs = sig.getInputsCount();
int i = 1;
System.out.println("MODEL SIGNATURE");
System.out.println("Inputs:");
for (Entry<String, TensorInfo> entry : sig.getInputsMap().entrySet()) {
TensorInfo t = entry.getValue();
System.out.printf(
"%d of %d: %-20s (Node name in graph: %-20s, type: %s)\n",
i++, numInputs, entry.getKey(), t.getName(), t.getDtype());
}
int numOutputs = sig.getOutputsCount();
i = 1;
System.out.println("Outputs:");
for (Entry<String, TensorInfo> entry : sig.getOutputsMap().entrySet()) {
TensorInfo t = entry.getValue();
System.out.printf(
"%d of %d: %-20s (Node name in graph: %-20s, type: %s)\n",
i++, numOutputs, entry.getKey(), t.getName(), t.getDtype());
}
System.out.println("-----------------------------------------------");
}
private static String[] loadLabels(String filename) throws Exception {
String text = new String(Files.readAllBytes(Paths.get(filename)), StandardCharsets.UTF_8);
StringIntLabelMap.Builder builder = StringIntLabelMap.newBuilder();
TextFormat.merge(text, builder);
StringIntLabelMap proto = builder.build();
int maxId = 0;
for (StringIntLabelMapItem item : proto.getItemList()) {
if (item.getId() > maxId) {
maxId = item.getId();
}
}
String[] ret = new String[maxId + 1];
for (StringIntLabelMapItem item : proto.getItemList()) {
ret[item.getId()] = item.getDisplayName();
}
return ret;
}
private static void bgr2rgb(byte[] data) {
for (int i = 0; i < data.length; i += 3) {
byte tmp = data[i];
data[i] = data[i + 2];
data[i + 2] = tmp;
}
}
public static BufferedImage bufferedImage(Mat m) {
int type = BufferedImage.TYPE_BYTE_GRAY;
if ( m.channels() > 1 ) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
BufferedImage image = new BufferedImage(m.cols(),m.rows(), type);
m.get(0,0,((DataBufferByte)image.getRaster().getDataBuffer()).getData()); // get all the pixels
return image;
}
private static Tensor<UInt8> makeImageTensor(Mat m) throws IOException {
BufferedImage img = bufferedImage(m);
if (img.getType() != BufferedImage.TYPE_3BYTE_BGR) {
throw new IOException(
String.format(
"Expected 3-byte BGR encoding in BufferedImage, found %d (file: %s). This code could be made more robust",
img.getType()));
}
byte[] data = ((DataBufferByte) img.getData().getDataBuffer()).getData();
bgr2rgb(data);
final long BATCH_SIZE = 1;
final long CHANNELS = 3;
long[] shape = new long[] {BATCH_SIZE, img.getHeight(), img.getWidth(), CHANNELS};
return Tensor.create(UInt8.class, shape, ByteBuffer.wrap(data));
}
private static void printUsage(PrintStream s) {
s.println("USAGE: <model> <label_map> <image> [<image>] [<image>]");
s.println("");
s.println("Where");
s.println("<model> is the path to the SavedModel directory of the model to use.");
s.println(" For example, the saved_model directory in tarballs from ");
s.println(
" https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md)");
s.println("");
s.println(
"<label_map> is the path to a file containing information about the labels detected by the model.");
s.println(" For example, one of the .pbtxt files from ");
s.println(
" https://github.com/tensorflow/models/tree/master/research/object_detection/data");
s.println("");
s.println("<image> is the path to an image file.");
s.println(" Sample images can be found from the COCO, Kitti, or Open Images dataset.");
s.println(
" See: https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md");
}
}
public static void main(String[] args) {
final BrokerHosts zkrHosts = new ZkHosts(args[0]);
final String kafkaTopic = args[1];
final String zkRoot = args[2];
final String clientId = args[3];
final SpoutConfig kafkaConf = new SpoutConfig(zkrHosts, kafkaTopic, zkRoot, clientId);
kafkaConf.fetchSizeBytes = 30971520;
kafkaConf.scheme = new SchemeAsMultiScheme(new StringScheme());
final TopologyBuilder topologyBuilder = new TopologyBuilder();
topologyBuilder.setSpout("kafka-spout", new KafkaSpout(kafkaConf), 1);
topologyBuilder.setBolt("print-messages", new PrinterBolt()).shuffleGrouping("kafka-spout");
final LocalCluster localCluster = new LocalCluster();
localCluster.submitTopology("kafka-topology", new HashMap<Object, Object>(), topologyBuilder.createTopology());
}
}
The above code is converted to jar using
mvn clean install shade:shade
When this jar is submitted to single node cluster using
storm -jar file.jar object_det.object_det.Objectdetect1
zookeeperhost:2181 topicname /brokers test
the code gets successfully executed.
But when the same jar is submitted in multi-node hadoop cluster ,the following error is shown
Running: /usr/jdk64/jdk1.8.0_112/bin/java -server -Ddaemon.name=
-Dstorm.options= -Dstorm.home=/usr/hdp/3.1.0.0-78/storm -Dstorm.log.dir=/var/log/storm -Djava.library.path=/usr/local/lib:/opt/local/lib:/usr/lib -Dstorm.conf.file= -cp /usr/hdp/3.1.0.0-78/storm/*:/usr/hdp/3.1.0.0-78/storm/lib/*:/usr/hdp/3.1.0.0-78/storm/extlib/* org.apache.storm.daemon.ClientJarTransformerRunner org.apache.storm.hack.StormShadeTransformer strmjr2-0.0.1-SNAPSHOT.jar /tmp/011dcea098a811e9b8d1f9e5e43755af.jar Exception in thread "main" java.lang.IllegalArgumentException at org.apache.storm.hack.shade.org.objectweb.asm.ClassReader.<init>(Unknown Source) at org.apache.storm.hack.shade.org.objectweb.asm.ClassReader.<init>(Unknown Source) at org.apache.storm.hack.shade.org.objectweb.asm.ClassReader.<init>(Unknown Source) at org.apache.storm.hack.DefaultShader.addRemappedClass(DefaultShader.java:182) at org.apache.storm.hack.DefaultShader.shadeJarStream(DefaultShader.java:103) at org.apache.storm.hack.StormShadeTransformer.transform(StormShadeTransformer.java:35) at org.apache.storm.daemon.ClientJarTransformerRunner.main(ClientJarTransformerRunner.java:37) Running: /usr/jdk64/jdk1.8.0_112/bin/java -Ddaemon.name=
-Dstorm.options= -Dstorm.home=/usr/hdp/3.1.0.0-78/storm -Dstorm.log.dir=/var/log/storm -Djava.library.path=/usr/local/lib:/opt/local/lib:/usr/lib -Dstorm.conf.file= -cp /usr/hdp/3.1.0.0-78/storm/*:/usr/hdp/3.1.0.0-78/storm/lib/*:/usr/hdp/3.1.0.0-78/storm/extlib/*:/tmp/011dcea098a811e9b8d1f9e5e43755af.jar:/usr/hdp/current/storm-supervisor/conf:/usr/hdp/3.1.0.0-78/storm/bin
-Dstorm.jar=/tmp/011dcea098a811e9b8d1f9e5e43755af.jar -Dstorm.dependency.jars= -Dstorm.dependency.artifacts={} artifactid.groupid.main
Error: Could not find or load main class
Is it possible to run a storm topology with tensorflow model in hadoop cluster.If yes, please help.
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import common.ResourcesToAccess;
public class RecordingStartingThread extends Thread{
#Override
public void run(){
JFrame f = new JFrame();
ImageIcon reel = new ImageIcon("src/images/reel.GIF");
JLabel label = new JLabel(reel);
reel.setImageObserver(label);
f.getContentPane().add(label);
f.setUndecorated(true);
f.setSize(300, 300);
f.setVisible(true);
}
public static void main(String[] args) {
new RecordingStartingThread().start();
}
}
Issue: GIF plays extremely fast.
Question: How do I make sure that GIF plays at a normal speed?
As for GIF speed playback - I've encountered this problem too. If I remember correctly this was caused by the "default" (or not provided?) value for frame rate in GIF file. Some web browsers "overrided" that frame rate so that GIF played correctly.
As a result I created a class that converts GIF (read GIF -> write GIF) and give frame rate provided by the user. com.madgag.gif.fmsware.AnimatedGifEncoder class is an external library that I link to the project via Maven: animated-gif-lib-1.0.jar
public final class GIFUtils {
private GIFUtils() {
}
public static List<BufferedImage> extractFrames(String filePath) throws IOException {
return extractFrames(new File(filePath));
}
public static List<BufferedImage> extractFrames(File file) throws IOException {
List<BufferedImage> imgs = new LinkedList<BufferedImage>();
ImageReader reader = ImageIO.getImageReadersBySuffix("GIF").next();
ImageInputStream in = null;
try {
in = ImageIO.createImageInputStream(new FileInputStream(file));
reader.setInput(in);
BufferedImage img = null;
int count = reader.getNumImages(true);
for(int i = 0; i < count; i++) {
Node tree = reader.getImageMetadata(i).getAsTree("javax_imageio_gif_image_1.0");
int x = Integer.valueOf(tree.getChildNodes().item(0).getAttributes()
.getNamedItem("imageLeftPosition").getNodeValue());
int y = Integer.valueOf(tree.getChildNodes().item(0).getAttributes()
.getNamedItem("imageTopPosition").getNodeValue());
BufferedImage image = reader.read(i);
if(img == null) {
img = new BufferedImage(image.getWidth() + x, image.getHeight() + y,
BufferedImage.TYPE_4BYTE_ABGR);
}
Graphics2D g = img.createGraphics();
ImageUtils.setBestRenderHints(g);
g.drawImage(image, x, y, null);
imgs.add(ImageUtils.copy(img));
}
}
finally {
if(in != null) {
in.close();
}
}
return imgs;
}
public static void writeGif(List<BufferedImage> images, File gifFile, int millisForFrame)
throws FileNotFoundException, IOException {
BufferedImage firstImage = images.get(0);
int type = firstImage.getType();
ImageOutputStream output = new FileImageOutputStream(gifFile);
// create a gif sequence with the type of the first image, 1 second
// between frames, which loops continuously
GifSequenceWriter writer = new GifSequenceWriter(output, type, 100, false);
// write out the first image to our sequence...
writer.writeToSequence(firstImage);
for(int i = 1; i < images.size(); i++) {
BufferedImage nextImage = images.get(i);
writer.writeToSequence(nextImage);
}
writer.close();
output.close();
}
public static Image createGif(List<BufferedImage> images, int millisForFrame) {
AnimatedGifEncoder g = new AnimatedGifEncoder();
ByteArrayOutputStream out = new ByteArrayOutputStream(5 * 1024 * 1024);
g.start(out);
g.setDelay(millisForFrame);
g.setRepeat(1);
for(BufferedImage i : images) {
g.addFrame(i);
}
g.finish();
byte[] bytes = out.toByteArray();
return Toolkit.getDefaultToolkit().createImage(bytes);
}
And GifSequenceWriter looks like this:
public class GifSequenceWriter {
protected ImageWriter gifWriter;
protected ImageWriteParam imageWriteParam;
protected IIOMetadata imageMetaData;
public GifSequenceWriter(ImageOutputStream outputStream, int imageType, int timeBetweenFramesMS,
boolean loopContinuously) throws IIOException, IOException {
gifWriter = getWriter();
imageWriteParam = gifWriter.getDefaultWriteParam();
ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(imageType);
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(timeBetweenFramesMS / 10));
graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0");
IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
commentsNode.setAttribute("CommentExtension", "Created by MAH");
IIOMetadataNode appEntensionsNode = getNode(root, "ApplicationExtensions");
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
child.setAttribute("applicationID", "NETSCAPE");
child.setAttribute("authenticationCode", "2.0");
int loop = loopContinuously ? 0 : 1;
child.setUserObject(new byte[] { 0x1, (byte) (loop & 0xFF), (byte) (loop >> 8 & 0xFF) });
appEntensionsNode.appendChild(child);
imageMetaData.setFromTree(metaFormatName, root);
gifWriter.setOutput(outputStream);
gifWriter.prepareWriteSequence(null);
}
public void writeToSequence(RenderedImage img) throws IOException {
gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam);
}
public void close() throws IOException {
gifWriter.endWriteSequence();
}
private static ImageWriter getWriter() throws IIOException {
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
if(!iter.hasNext()) {
throw new IIOException("No GIF Image Writers Exist");
}
return iter.next();
}
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;
}
}
The easiest fix for this problem is to just create a .gif file which is "well-formed", i.e. contains the appropriate frame rate. This can be achieved with the help of various online converters, just google "gif speed changer".