Skip to content

Add option to extract line-based bounding boxes from pdfminer.#874

Merged
bsowell merged 5 commits into
mainfrom
pdfminer_lines
Oct 11, 2024
Merged

Add option to extract line-based bounding boxes from pdfminer.#874
bsowell merged 5 commits into
mainfrom
pdfminer_lines

Conversation

@bsowell
Copy link
Copy Markdown
Contributor

@bsowell bsowell commented Oct 4, 2024

We have been using pdfminer's layout detection to group text into boxes. This can cause issues, especially with table extraction, when the boxes don't line up with cells or what we detect with the DETR model. This change adds support for an object_type parameter to the PdfMinerExtractor that can be set to "boxes" (the current behavior), or "lines", which groups characters into lines, but does not group them further.

To avoid an explosion of options, we introduce a
"text_extractor_options" dict as a paramter, and refactor the TextExtractor class hierarchy a bit to support it.

Copy link
Copy Markdown
Contributor

@karanataryn karanataryn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks mostly good to me. You'll need to merge in main which might require some refactoring around #894 and #895. Do we want to add a regression test that shows improvement (text that exists now because of line-based pdfminer)?

# I was very surprised that this equality succeeded. I'm not sure in general we can expect
# exact text equality. I imagine in some cases the order might be different, but in this case
# they match, so I'm asserting here so we can catch regressions.
assert lines_text == objects_text
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that we have empty lines_elements since len(objects_elements) < len(lines_elements)? And if so, do we have a way to deal with that on the customer side?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean by having empty lines_elements. Since each box is a grouping of lines, it is always the case that len(lines) >= len(objects) -- I should probably have called it boxes instead of objects here. I made the inequality strict to ensure that we are actually doing something different. The user should not notice. By the time it gets to them, either lines or boxes will be merged into elements.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should have phrased it differently, I mean that since lines_text == objects_text and len(lines_elements) > len(objects_elements), we would have some elements that don't have any text in them. Shouldn't be a real performance issue but wanted to flag it for the customer side.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the common case is that all the elements have text, just that the lines_elements have less text. But again, these are the elements that come directly out of pdfminer. They get grouped into larger elements in _suplement_text before they get returned to the users. So users shouldn't see a difference.

class PdfMinerExtractor(TextExtractorBase):
@requires_modules(["pdfminer", "pdfminer.utils"], extra="local-inference")
def __init__(self):
def __init__(self, object_type: Literal["boxes", "lines"] = "boxes"):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure we want the default to always stay as boxes? Would we want to enable lines if we detect a table element in the DETR output?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine eventually we will want to change the default to lines. I just started with the current behavior until we are confident we won't see any regressions in either perf or quality.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Could you add a TODO to get back to this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Will do.

Comment thread lib/sycamore/sycamore/transforms/text_extraction/pdf_miner.py Outdated
Comment thread lib/sycamore/sycamore/transforms/text_extraction/__init__.py Outdated
Copy link
Copy Markdown
Contributor

@karanataryn karanataryn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Few suggestions but not blocking.

if not model_cls:
raise ValueError(f"Unknown OCR Model: {ocr_model}")
ocr_model_obj = model_cls()
ocr_model_obj = cast(OcrModel, get_text_extractor(ocr_model, **text_extraction_options))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to assert instead of cast? We can't handle a non OcrModel

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, let me fix this.

class PdfMinerExtractor(TextExtractorBase):
@requires_modules(["pdfminer", "pdfminer.utils"], extra="local-inference")
def __init__(self):
def __init__(self, object_type: Literal["boxes", "lines"] = "boxes"):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Could you add a TODO to get back to this?

# I was very surprised that this equality succeeded. I'm not sure in general we can expect
# exact text equality. I imagine in some cases the order might be different, but in this case
# they match, so I'm asserting here so we can catch regressions.
assert lines_text == objects_text
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should have phrased it differently, I mean that since lines_text == objects_text and len(lines_elements) > len(objects_elements), we would have some elements that don't have any text in them. Shouldn't be a real performance issue but wanted to flag it for the customer side.

Comment thread lib/sycamore/sycamore/transforms/detr_partitioner.py Outdated
We have been using pdfminer's layout detection to group text into
boxes. This can cause issues, especially with table extraction, when
the boxes don't line up with cells or what we detect with the DETR
model. This change adds support for an object_type parameter to the
PdfMinerExtractor that can be set to "boxes" (the current behavior),
or "lines", which groups characters into lines, but does not group
them further.

To avoid an explosion of options, we introduce a
"text_extractor_options" dict as a paramter, and refactor the
TextExtractor class hierarchy a bit to support it.
Will add back in later commit when I add the format detection stuff.
@bsowell bsowell merged commit 9b0ddc6 into main Oct 11, 2024
@bsowell bsowell deleted the pdfminer_lines branch October 11, 2024 22:23
dtecuci pushed a commit that referenced this pull request Oct 14, 2024
We have been using pdfminer's layout detection to group text into
boxes. This can cause issues, especially with table extraction, when
the boxes don't line up with cells or what we detect with the DETR
model. This change adds support for an object_type parameter to the
PdfMinerExtractor that can be set to "boxes" (the current behavior),
or "lines", which groups characters into lines, but does not group
them further.

To avoid an explosion of options, we introduce a
"text_extractor_options" dict as a paramter, and refactor the
TextExtractor class hierarchy a bit to support it.
dtecuci added a commit that referenced this pull request Oct 14, 2024
* added ability to read schema from file

* small typo

Co-authored-by: Matt Welsh <matt@aryn.ai>

* fixed two funtion refs that were modified

* reformatted file with black

* fixed schema file format (was json), added more exception handling

* Fix anonymous reading in materialize and add rate limited logging. (#898)

* Fix anonymous reading in materialize and add rate limited logging.

* In materialize, try reading using the credentials, but if it doesn't work, fall back to
  reading anonymously if that seems to be working.
* Add rate limited logging to reading via materialize in local mode.
* Check for no root before checking if a source since that makes more sense.
* switch ntsb_loader_materialized.py over to read in local mode, it was working (with the anonymous
  fix), but was very slow hence the logging.

* Bump version to v0.1.23. (#903)

* fix asdict in the reader too. duh (#907)

Signed-off-by: Henry Lindeman <hmlindeman@yahoo.com>

* Add text reprentation for empty tables (#909)

* Refactor logical plan serialization. (#905)

* Working on this.

* Working on refactoring.

* Tests pass - is such a thing even possible?

* Fix tests.

* Fix mypy.

* Cleanup.

* Fix NTSB examples.

* A few tweaks to the query planner prompt, and a workaround in
queryui/util.py.

* Fix mypy.

* seriously small performance improvement that matters when youre processing tens of thousands of tables (from training code) (#906)

Signed-off-by: Henry Lindeman <hmlindeman@yahoo.com>

* Handle opensearch reader doc resconstruction when no parent doc in results (#908)

* Fix bug in entity extraction. (#911)

* Notebooks like default-prep-script.ipynb would fail because the wrong way of generating the prompt would be used.
* Rename test to match with name of file being tested.
* Fix existing tests to verify parameters on all branches -- the reason the tests were passing
  was that it was taking the default branch in the test cases
* Update all of the tests to directly call run rather than route everything through ray.

* Enable copying of the hash context. (#910)

* Enable copying of the hash context.

* Address comments.

* Add option to extract line-based bounding boxes from pdfminer. (#874)

We have been using pdfminer's layout detection to group text into
boxes. This can cause issues, especially with table extraction, when
the boxes don't line up with cells or what we detect with the DETR
model. This change adds support for an object_type parameter to the
PdfMinerExtractor that can be set to "boxes" (the current behavior),
or "lines", which groups characters into lines, but does not group
them further.

To avoid an explosion of options, we introduce a
"text_extractor_options" dict as a paramter, and refactor the
TextExtractor class hierarchy a bit to support it.

* Support random sample in local mode. (#913)

This transform isn't widely used, but still worth supporting in local
model to bring it to parity.

* Opensearch kwargs fix (#914)

* Fix kwargs in opensearch reader

* simplify test assertion

* lint

* pr comments

* fix typo (#917)

* Update using_jupyter.md (#902)

* Update using_jupyter.md

Update link

* Fixed path

---------

Co-authored-by: dtecuci <168428824+dtecuci@users.noreply.github.com>

* Rebased. Added ability to read schema from file

* rebased. small typo

Co-authored-by: Matt Welsh <matt@aryn.ai>

* rebased. reformatted file with black

* resolved conflicts

* changed schema file format to yaml

* removed unused import

* small typos fixed

* fixed spacing

---------

Signed-off-by: Henry Lindeman <hmlindeman@yahoo.com>
Co-authored-by: Matt Welsh <matt@aryn.ai>
Co-authored-by: Eric Anderson <eric@aryn.ai>
Co-authored-by: Ben Sowell <ben@aryn.ai>
Co-authored-by: Henry Lindeman <hmlindeman@yahoo.com>
Co-authored-by: Dhruv Kaliraman <112497058+dhruvkaliraman7@users.noreply.github.com>
Co-authored-by: Vinayak Thapliyal <vinayak@aryn.ai>
Co-authored-by: Alex Meyer <144723289+alexaryn@users.noreply.github.com>
Co-authored-by: Karan Sampath <176953591+karanataryn@users.noreply.github.com>
Co-authored-by: jonfritz <134336691+jonfritz@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants