diff --git a/CHANGES.txt b/CHANGES.txt
index bf320ab3af8..06d73730b4f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -13,6 +13,8 @@ Release 4.0.0-BETA1 - ???
* Removed the dotnet module (TIKA-4332).
+ * Removed the advanced media module (TIKA-4500).
+
* Remove the tika-dl module (TIKA-4499).
OTHER CHANGES
diff --git a/assembly.xml b/assembly.xml
index 4095cac5617..f69a4af6238 100644
--- a/assembly.xml
+++ b/assembly.xml
@@ -28,7 +28,6 @@
**/.*/**
**/opennlp/ner-*.bin
**/opennlp/en-*.bin
- **/recognition/*.bin
**/*.releaseBackup
diff --git a/tika-parsers/tika-parsers-ml/pom.xml b/tika-parsers/tika-parsers-ml/pom.xml
index 0f9a5f04803..8f2a98b0ed8 100644
--- a/tika-parsers/tika-parsers-ml/pom.xml
+++ b/tika-parsers/tika-parsers-ml/pom.xml
@@ -93,19 +93,6 @@
-
-
- sandbox
-
-
- tika-age-recogniser
- tika-parser-advancedmedia-module
- tika-parser-advancedmedia-package
-
-
-
-
3.0.0-rc1
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/pom.xml b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/pom.xml
deleted file mode 100644
index 73211f6cb12..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/pom.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
- tika-parsers-ml
- org.apache.tika
- 4.0.0-SNAPSHOT
-
- 4.0.0
-
- tika-parser-advancedmedia-module
- Apache Tika advanced media module
-
-
-
- ${project.groupId}
- tika-parser-audiovideo-module
- ${project.version}
-
-
- com.googlecode.json-simple
- json-simple
-
-
- org.apache.httpcomponents
- httpclient
-
-
- org.apache.httpcomponents
- httpcore
-
-
- commons-codec
- commons-codec
-
-
- org.apache.httpcomponents
- httpmime
-
-
- com.github.openjson
- openjson
- ${openjson.version}
-
-
- jakarta.ws.rs
- jakarta.ws.rs-api
-
-
- org.apache.commons
- commons-exec
-
-
- org.apache.commons
- commons-lang3
- test
-
-
- org.apache.cxf
- cxf-rt-rs-client
- test
-
-
-
-
- 3.0.0-rc1
-
-
\ No newline at end of file
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/captioning/CaptionObject.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/captioning/CaptionObject.java
deleted file mode 100644
index 26c19901a54..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/captioning/CaptionObject.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tika.parser.captioning;
-
-import org.apache.tika.parser.recognition.RecognisedObject;
-
-/**
- * A model for caption objects from graphics and texts typically includes
- * human readable sentence, language of the sentence and confidence score.
- *
- * @since Apache Tika 1.16
- */
-public class CaptionObject extends RecognisedObject {
-
- public CaptionObject(String sentence, String sentenceLang, double confidence) {
- super(sentence, sentenceLang, null, confidence);
- }
-
- @Override
- public String toString() {
- return "Caption{" + "sentence='" + label + "\' (" + labelLang + ')' + ", confidence=" +
- confidence + '}';
- }
-
-}
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/captioning/tf/TensorflowRESTCaptioner.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/captioning/tf/TensorflowRESTCaptioner.java
deleted file mode 100644
index 8d3320dea1e..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/captioning/tf/TensorflowRESTCaptioner.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tika.parser.captioning.tf;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
-import org.json.simple.parser.JSONParser;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import org.apache.tika.config.Field;
-import org.apache.tika.config.InitializableProblemHandler;
-import org.apache.tika.config.Param;
-import org.apache.tika.exception.TikaConfigException;
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.mime.MediaType;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.parser.captioning.CaptionObject;
-import org.apache.tika.parser.recognition.ObjectRecogniser;
-
-/**
- * Tensorflow image captioner.
- * This implementation uses Tensorflow via REST API.
- *
- * NOTE : https://wiki.apache.org/tika/ImageCaption
- *
- * @since Apache Tika 1.17
- */
-public class TensorflowRESTCaptioner implements ObjectRecogniser {
- private static final Logger LOG = LoggerFactory.getLogger(TensorflowRESTCaptioner.class);
-
- private static final Set SUPPORTED_MIMES = Collections.unmodifiableSet(new HashSet<>(
- Arrays.asList(new MediaType[]{MediaType.image("jpeg"), MediaType.image("png"),
- MediaType.image("gif")})));
-
- private static final String LABEL_LANG = "eng";
-
- @Field
- private URI apiBaseUri = URI.create("http://localhost:8764/inception/v3");
-
- @Field
- private int captions = 5;
-
- @Field
- private int maxCaptionLength = 15;
-
- private URI apiUri;
-
- private URI healthUri;
-
- private boolean available;
-
- protected URI getApiUri(Metadata metadata) {
- return apiUri;
- }
-
- @Override
- public Set getSupportedMimes() {
- return SUPPORTED_MIMES;
- }
-
- @Override
- public boolean isAvailable() {
- return available;
- }
-
- @Override
- public void initialize(Map params) throws TikaConfigException {
- healthUri = URI.create(apiBaseUri + "/ping");
- apiUri = URI.create(apiBaseUri + String.format(Locale.getDefault(),
- "/caption/image?beam_size=%1$d&max_caption_length=%2$d", captions,
- maxCaptionLength));
-
- try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
- HttpResponse response = client.execute(new HttpGet(healthUri));
- available = response.getStatusLine().getStatusCode() == 200;
-
- LOG.info("Available = {}, API Status = {}", available, response.getStatusLine());
- LOG.info("Captions = {}, MaxCaptionLength = {}", captions, maxCaptionLength);
- } catch (Exception e) {
- available = false;
- throw new TikaConfigException(e.getMessage(), e);
- }
- }
-
- @Override
- public void checkInitialization(InitializableProblemHandler handler)
- throws TikaConfigException {
- //TODO -- what do we want to check?
- }
-
- @Override
- public List recognise(InputStream stream, ContentHandler handler,
- Metadata metadata, ParseContext context)
- throws IOException, SAXException, TikaException {
- List capObjs = new ArrayList<>();
- try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
-
- HttpPost request = new HttpPost(getApiUri(metadata));
-
- try (UnsynchronizedByteArrayOutputStream byteStream = UnsynchronizedByteArrayOutputStream.builder().get()) {
- //TODO: convert this to stream, this might cause OOM issue
- // InputStreamEntity is not working
- // request.setEntity(new InputStreamEntity(stream, -1));
- IOUtils.copy(stream, byteStream);
- request.setEntity(new ByteArrayEntity(byteStream.toByteArray()));
- }
-
- HttpResponse response = client.execute(request);
- try (InputStream reply = response.getEntity().getContent()) {
- String replyMessage = IOUtils.toString(reply, StandardCharsets.UTF_8);
- if (response.getStatusLine().getStatusCode() == 200) {
- JSONObject jReply = (JSONObject) new JSONParser().parse(replyMessage);
- JSONArray jCaptions = (JSONArray) jReply.get("captions");
- for (Object caption : jCaptions) {
- JSONObject jCaption = (JSONObject) caption;
- String sentence = (String) jCaption.get("sentence");
- Double confidence = (Double) jCaption.get("confidence");
- capObjs.add(new CaptionObject(sentence, LABEL_LANG, confidence));
- }
- } else {
- LOG.warn("Status = {}", response.getStatusLine());
- LOG.warn("Response = {}", replyMessage);
- }
- }
- } catch (Exception e) {
- LOG.warn(e.getMessage(), e);
- }
- return capObjs;
- }
-}
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/pot/PooledTimeSeriesParser.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/pot/PooledTimeSeriesParser.java
deleted file mode 100644
index ceaec071da6..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/pot/PooledTimeSeriesParser.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tika.parser.pot;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.time.Duration;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.commons.exec.CommandLine;
-import org.apache.commons.exec.DefaultExecutor;
-import org.apache.commons.exec.ExecuteWatchdog;
-import org.apache.commons.exec.PumpStreamHandler;
-import org.apache.commons.exec.environment.EnvironmentUtils;
-import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.io.TemporaryResources;
-import org.apache.tika.io.TikaInputStream;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.mime.MediaType;
-import org.apache.tika.mime.MediaTypeRegistry;
-import org.apache.tika.parser.CompositeParser;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.parser.Parser;
-import org.apache.tika.parser.external.ExternalParser;
-import org.apache.tika.parser.mp4.MP4Parser;
-import org.apache.tika.sax.XHTMLContentHandler;
-
-/**
- * Uses the Pooled Time Series algorithm + command line tool, to
- * generate a numeric representation of the video suitable for
- * similarity searches.
- * See https://wiki.apache.org/tika/PooledTimeSeriesParser for
- * more details and setup instructions.
- */
-public class PooledTimeSeriesParser implements Parser {
-
- static final boolean isAvailable =
- ExternalParser.check(new String[]{"pooled-time-series", "--help"}, -1);
- private static final long serialVersionUID = -2855917932512164988L;
- private static final Set SUPPORTED_TYPES = isAvailable ? Collections.unmodifiableSet(
- new HashSet<>(Arrays.asList(
- new MediaType[]{MediaType.video("avi"), MediaType.video("mp4")}))) :
- Collections.emptySet();
- // TODO: Add all supported video types
-
- private static final Logger LOG = LoggerFactory.getLogger(PooledTimeSeriesParser.class);
- // TIKA-1445 workaround parser
- private static Parser _TMP_VIDEO_METADATA_PARSER = new CompositeVideoParser();
-
- /**
- * Returns the set of media types supported by this parser when used with the
- * given parse context.
- *
- * @param context parse context
- * @return immutable set of media types
- * @since Apache Tika 0.7
- */
- @Override
- public Set getSupportedTypes(ParseContext context) {
- return SUPPORTED_TYPES;
- }
-
- /**
- * Parses a document stream into a sequence of XHTML SAX events. Fills in
- * related document metadata in the given metadata object.
- *
- * The given document stream is consumed but not closed by this method. The
- * responsibility to close the stream remains on the caller.
- *
- * Information about the parsing context can be passed in the context
- * parameter. See the parser implementations for the kinds of context
- * information they expect.
- *
- * @param stream the document stream (input)
- * @param handler handler for the XHTML SAX events (output)
- * @param metadata document metadata (input and output)
- * @param context parse context
- * @throws IOException if the document stream could not be read
- * @throws SAXException if the SAX events could not be processed
- * @throws TikaException if the document could not be parsed
- * @since Apache Tika 0.5
- */
- @Override
- public void parse(InputStream stream, ContentHandler handler, Metadata metadata,
- ParseContext context) throws IOException, SAXException, TikaException {
-
- if (!isAvailable) {
- LOG.warn("PooledTimeSeries not installed!");
- return;
- }
-
- XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata);
-
- TemporaryResources tmp = new TemporaryResources();
- try {
- TikaInputStream tikaStream = TikaInputStream.get(stream, tmp, metadata);
- File input = tikaStream.getFile();
- String cmdOutput = computePoT(input);
- try (InputStream ofStream = new FileInputStream(
- new File(input.getAbsoluteFile() + ".of.txt"))) {
- try (InputStream ogStream = new FileInputStream(
- new File(input.getAbsoluteFile() + ".hog.txt"))) {
-
- extractHeaderOutput(ofStream, metadata, "of");
- extractHeaderOutput(ogStream, metadata, "og");
- xhtml.startDocument();
- doExtract(ofStream, xhtml, "Histogram of Optical Flows (HOF)",
- metadata.get("of_frames"), metadata.get("of_vecSize"));
- doExtract(ogStream, xhtml, "Histogram of Oriented Gradients (HOG)",
- metadata.get("og_frames"), metadata.get("og_vecSize"));
- xhtml.endDocument();
- }
- }
- // Temporary workaround for TIKA-1445 - until we can specify
- // composite parsers with strategies (eg Composite, Try In Turn),
- // always send the image onwards to the regular parser to have
- // the metadata for them extracted as well
- _TMP_VIDEO_METADATA_PARSER.parse(tikaStream, handler, metadata, context);
-
- } finally {
- tmp.dispose();
- }
- }
-
- private String computePoT(File input) throws IOException {
-
- CommandLine cmdLine = new CommandLine("pooled-time-series");
- try (UnsynchronizedByteArrayOutputStream outputStream = UnsynchronizedByteArrayOutputStream.builder().get()) {
- cmdLine.addArgument("-f");
- cmdLine.addArgument(input.getAbsolutePath());
- LOG.trace("Executing: {}", cmdLine);
- DefaultExecutor exec = DefaultExecutor.builder().get();
- exec.setExitValue(0);
- ExecuteWatchdog watchdog = ExecuteWatchdog.builder().setTimeout(Duration.ofMillis(60000)).get();
- exec.setWatchdog(watchdog);
- PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
- exec.setStreamHandler(streamHandler);
- int exitValue = exec.execute(cmdLine, EnvironmentUtils.getProcEnvironment());
- return outputStream.toString(UTF_8);
- }
- }
-
- /**
- * Reads the contents of the given stream and write it to the given XHTML
- * content handler. The stream is closed once fully processed.
- *
- * @param stream Stream where is the result of ocr
- * @param xhtml XHTML content handler
- * @param tableTitle The name of the matrix/table to display.
- * @param frames Number of frames read from the video.
- * @param vecSize Size of the OF or HOG vector.
- * @throws SAXException if the XHTML SAX events could not be handled
- * @throws IOException if an input error occurred
- */
- private void doExtract(InputStream stream, XHTMLContentHandler xhtml, String tableTitle,
- String frames, String vecSize) throws SAXException, IOException {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, UTF_8))) {
- String line = null;
- AttributesImpl attributes = new AttributesImpl();
- attributes.addAttribute("", "", "rows", "CDATA", frames);
- attributes.addAttribute("", "", "cols", "CDATA", vecSize);
-
- xhtml.startElement("h3");
- xhtml.characters(tableTitle);
- xhtml.endElement("h3");
- xhtml.startElement("table", attributes);
- while ((line = reader.readLine()) != null) {
- xhtml.startElement("tr");
- for (String val : line.split(" ")) {
- xhtml.startElement("td");
- xhtml.characters(val);
- xhtml.endElement("td");
- }
- xhtml.endElement("tr");
- }
- xhtml.endElement("table");
- }
- }
-
- private void extractHeaderOutput(InputStream stream, Metadata metadata, String prefix)
- throws IOException {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, UTF_8))) {
- String line = reader.readLine();
- String[] firstLine = line.split(" ");
- String frames = firstLine[0];
- String vecSize = firstLine[1];
-
- if (prefix == null) {
- prefix = "";
- }
- metadata.add(prefix + "_frames", frames);
- metadata.add(prefix + "_vecSize", vecSize);
- }
- }
-
- private static class CompositeVideoParser extends CompositeParser {
- private static final long serialVersionUID = -2398203965206381382L;
- private static List videoParsers = Arrays.asList(new Parser[]{new MP4Parser()});
-
- CompositeVideoParser() {
- super(new MediaTypeRegistry(), videoParsers);
- }
- }
-
-}
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/ObjectRecogniser.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/ObjectRecogniser.java
deleted file mode 100644
index 97b4d4d67e7..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/ObjectRecogniser.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tika.parser.recognition;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import org.apache.tika.config.Initializable;
-import org.apache.tika.config.Param;
-import org.apache.tika.exception.TikaConfigException;
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.mime.MediaType;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.parser.recognition.tf.TensorflowImageRecParser;
-
-/**
- * This is a contract for object recognisers used by {@link ObjectRecognitionParser}
- *
- * @see TensorflowImageRecParser
- */
-public interface ObjectRecogniser extends Initializable {
-
- /**
- * The mimes supported by this recogniser
- *
- * @return set of mediatypes
- */
- Set getSupportedMimes();
-
- /**
- * Is this service available
- *
- * @return {@code true} when the service is available, {@code false} otherwise
- */
- boolean isAvailable();
-
- /**
- * This is the hook for configuring the recogniser
- *
- * @param params configuration instance in the form of context
- * @throws TikaConfigException when there is an issue with configuration
- */
- void initialize(Map params) throws TikaConfigException;
-
- /**
- * Recognise the objects in the stream
- *
- * @param stream content stream
- * @param handler tika's content handler
- * @param metadata metadata instance
- * @param context parser context
- * @return List of {@link RecognisedObject}s
- * @throws IOException when an I/O error occurs
- * @throws SAXException when an issue with XML occurs
- * @throws TikaException any generic error
- */
- List extends RecognisedObject> recognise(InputStream stream, ContentHandler handler,
- Metadata metadata, ParseContext context)
- throws IOException, SAXException, TikaException;
-}
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/ObjectRecognitionParser.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/ObjectRecognitionParser.java
deleted file mode 100644
index 289289a74e3..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/ObjectRecognitionParser.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tika.parser.recognition;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import org.apache.tika.config.Field;
-import org.apache.tika.config.Initializable;
-import org.apache.tika.config.InitializableProblemHandler;
-import org.apache.tika.config.Param;
-import org.apache.tika.exception.TikaConfigException;
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.mime.MediaType;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.parser.Parser;
-import org.apache.tika.parser.captioning.CaptionObject;
-import org.apache.tika.sax.XHTMLContentHandler;
-import org.apache.tika.utils.AnnotationUtils;
-import org.apache.tika.utils.ServiceLoaderUtils;
-
-
-/**
- * This parser recognises objects from Images.
- * The Object Recognition implementation can be switched using 'class' argument.
- *
- * Example Usage :
- *
- * <properties>
- * <parsers>
- * <parser class="org.apache.tika.parser.recognition.ObjectRecognitionParser">
- * <params>
- * <param name="class" type="string">
- * org.apache.tika.parser.recognition.tf.TensorflowRESTRecogniser</param>
- * <param name="class" type="string">
- * org.apache.tika.parser.captioning.tf.TensorflowRESTCaptioner</param>
- * </params>
- * </parser>
- * </parsers>
- * </properties>
- *
- *
- * @since Apache Tika 1.14
- */
-public class ObjectRecognitionParser implements Parser, Initializable {
- public static final String MD_KEY_OBJ_REC = "OBJECT";
- public static final String MD_KEY_IMG_CAP = "CAPTION";
- public static final String MD_REC_IMPL_KEY =
- ObjectRecognitionParser.class.getPackage().getName() + ".object.rec.impl";
- private static final Logger LOG = LoggerFactory.getLogger(ObjectRecognitionParser.class);
- private ObjectRecogniser recogniser;
-
- @Field(name = "class")
- public void setRecogniser(String recogniserClass) {
- this.recogniser = ServiceLoaderUtils.newInstance(recogniserClass);
- }
-
- @Override
- public void initialize(Map params) throws TikaConfigException {
- AnnotationUtils.assignFieldParams(recogniser, params);
- recogniser.initialize(params);
- LOG.info("Recogniser = {}", recogniser.getClass().getName());
- LOG.info("Recogniser Available = {}", recogniser.isAvailable());
- }
-
- @Override
- public void checkInitialization(InitializableProblemHandler handler)
- throws TikaConfigException {
- //TODO -- what do we want to check?
- }
-
- @Override
- public Set getSupportedTypes(ParseContext context) {
- return recogniser.isAvailable() ? recogniser.getSupportedMimes() :
- Collections.emptySet();
- }
-
- @Override
- public synchronized void parse(InputStream stream, ContentHandler handler, Metadata metadata,
- ParseContext context)
- throws IOException, SAXException, TikaException {
- if (!recogniser.isAvailable()) {
- LOG.warn("{} is not available for service", recogniser.getClass());
- return;
- }
- metadata.set(MD_REC_IMPL_KEY, recogniser.getClass().getName());
- long start = System.currentTimeMillis();
- List extends RecognisedObject> objects =
- recogniser.recognise(stream, handler, metadata, context);
-
- LOG.debug("Found {} objects", objects != null ? objects.size() : 0);
- LOG.info("Time taken {}ms", System.currentTimeMillis() - start);
-
- if (objects != null && !objects.isEmpty()) {
- int count;
- List acceptedObjects = new ArrayList<>();
- List xhtmlIds = new ArrayList<>();
- String xhtmlStartVal = null;
- count = 0;
- objects.sort((o1, o2) -> Double.compare(o2.getConfidence(), o1.getConfidence()));
- // first process all the MD objects
- for (RecognisedObject object : objects) {
- if (object instanceof CaptionObject) {
- if (xhtmlStartVal == null) {
- xhtmlStartVal = "captions";
- }
- String labelAndConfidence =
- String.format(Locale.ENGLISH, "%s (%.5f)", object.getLabel(),
- object.getConfidence());
- metadata.add(MD_KEY_IMG_CAP, labelAndConfidence);
- xhtmlIds.add(String.valueOf(count++));
- } else {
- if (xhtmlStartVal == null) {
- xhtmlStartVal = "objects";
- }
- String labelAndConfidence =
- String.format(Locale.ENGLISH, "%s (%.5f)", object.getLabel(),
- object.getConfidence());
- metadata.add(MD_KEY_OBJ_REC, labelAndConfidence);
- xhtmlIds.add(object.getId());
- }
- LOG.info("Add {}", object);
- acceptedObjects.add(object);
- }
- XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata);
- xhtml.startDocument();
- xhtml.startElement("ol", "id", xhtmlStartVal);
- count = 0;
- for (RecognisedObject object : acceptedObjects) {
- //writing to handler
- xhtml.startElement("li", "id", xhtmlIds.get(count++));
- String text = String.format(Locale.ENGLISH, " %s [%s](confidence = %f)",
- object.getLabel(), object.getLabelLang(), object.getConfidence());
- xhtml.characters(text);
- xhtml.endElement("li");
- }
- xhtml.endElement("ol");
- xhtml.endDocument();
- } else {
- LOG.warn("NO objects");
- metadata.add("no.objects", Boolean.TRUE.toString());
- }
- }
-}
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/RecognisedObject.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/RecognisedObject.java
deleted file mode 100644
index b2b841dbaad..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/RecognisedObject.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tika.parser.recognition;
-
-/**
- * A model for recognised objects from graphics and texts typically includes
- * human readable label for the object, language of the label, id and confidence score.
- *
- * @since Apache Tika 1.14
- */
-public class RecognisedObject {
-
- /**
- * Label of this object. Usually the name given to this object by humans
- */
- protected String label;
- /**
- * Language of label, Example : english
- */
- protected String labelLang;
- /**
- * Identifier for this object
- */
- protected String id;
- /**
- * Confidence score
- */
- protected double confidence;
-
- public RecognisedObject(String label, String labelLang, String id, double confidence) {
- this.label = label;
- this.labelLang = labelLang;
- this.id = id;
- this.confidence = confidence;
- }
-
- public String getLabel() {
- return label;
- }
-
- public void setLabel(String label) {
- this.label = label;
- }
-
- public String getLabelLang() {
- return labelLang;
- }
-
- public void setLabelLang(String labelLang) {
- this.labelLang = labelLang;
- }
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public double getConfidence() {
- return confidence;
- }
-
- public void setConfidence(double confidence) {
- this.confidence = confidence;
- }
-
- @Override
- public String toString() {
- return "RecognisedObject{" + "label='" + label + "\' (" + labelLang + ')' + ", id='" + id +
- '\'' + ", confidence=" + confidence + '}';
- }
-}
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/tf/TensorflowImageRecParser.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/tf/TensorflowImageRecParser.java
deleted file mode 100644
index fd989cbf033..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/tf/TensorflowImageRecParser.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tika.parser.recognition.tf;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import org.apache.tika.config.Field;
-import org.apache.tika.config.InitializableProblemHandler;
-import org.apache.tika.config.Param;
-import org.apache.tika.exception.TikaConfigException;
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.mime.MediaType;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.parser.external.ExternalParser;
-import org.apache.tika.parser.recognition.ObjectRecogniser;
-import org.apache.tika.parser.recognition.RecognisedObject;
-
-/**
- * This is an implementation of {@link ObjectRecogniser} powered by
- * Tensorflow
- * convolutional neural network (CNN). This implementation binds to
- * Python API using {@link ExternalParser}.
- *
- * // NOTE: This is a proof of concept for an efficient implementation using JNI binding to
- * Tensorflow's C++ api.
- *
- *
- *
- * b>Environment Setup:
- *
- * - Python must be available
- * - Tensorflow must be available for import by the python script.
- * Setup Instructions here
- * - All dependencies of tensor flow (such as numpy) must also be available.
- * Follow the image recognition
- * guide and make sure it works
- *
- *
- *
- * @see TensorflowRESTRecogniser
- * @since Apache Tika 1.14
- */
-public class TensorflowImageRecParser extends ExternalParser implements ObjectRecogniser {
- static final Set SUPPORTED_MIMES = Collections.singleton(MediaType.image("jpeg"));
- private static final Logger LOG = LoggerFactory.getLogger(TensorflowImageRecParser.class);
- private static final String SCRIPT_FILE_NAME = "classify_image.py";
- private static final File DEFAULT_SCRIPT_FILE =
- new File("tensorflow" + File.separator + SCRIPT_FILE_NAME);
- private static final File DEFAULT_MODEL_FILE =
- new File("tensorflow" + File.separator + "tf-objectrec-model");
- private static final LineConsumer IGNORED_LINE_LOGGER = LOG::debug;
-
- @Field
- private String executor = "python";
- @Field
- private File scriptFile = DEFAULT_SCRIPT_FILE;
- @Field
- private String modelArg = "--model_dir";
- @Field
- private File modelFile = DEFAULT_MODEL_FILE;
- @Field
- private String imageArg = "--image_file";
- @Field
- private String outPattern = "(.*) \\(score = ([0-9]+\\.[0-9]+)\\)$";
- @Field
- private String availabilityTestArgs = ""; //when no args are given, the script will test itself!
-
- private boolean available = false;
-
- public Set getSupportedMimes() {
- return SUPPORTED_MIMES;
- }
-
- @Override
- public boolean isAvailable() {
- return available;
- }
-
- @Override
- public void initialize(Map params) throws TikaConfigException {
- try {
- if (!modelFile.exists()) {
- modelFile.getParentFile().mkdirs();
- LOG.warn("Model doesn't exist at {}. Expecting the script to download it.",
- modelFile);
- }
- if (!scriptFile.exists()) {
- scriptFile.getParentFile().mkdirs();
- LOG.info("Copying script to : {}", scriptFile);
- try (InputStream sourceStream = getClass().getResourceAsStream(SCRIPT_FILE_NAME)) {
- try (OutputStream destStream = new FileOutputStream(scriptFile)) {
- IOUtils.copy(sourceStream, destStream);
- }
- }
- LOG.debug("Copied..");
- }
- String[] availabilityCheckArgs =
- {executor, scriptFile.getAbsolutePath(), modelArg, modelFile.getAbsolutePath(),
- availabilityTestArgs};
- available = ExternalParser.check(availabilityCheckArgs);
- LOG.debug("Available? {}", available);
- if (!available) {
- return;
- }
- String[] parseCmd =
- {executor, scriptFile.getAbsolutePath(), modelArg, modelFile.getAbsolutePath(),
- imageArg, INPUT_FILE_TOKEN, "--out_file",
- OUTPUT_FILE_TOKEN}; //inserting output token to let
- // external parser parse metadata
- setCommand(parseCmd);
- HashMap patterns = new HashMap<>();
- patterns.put(Pattern.compile(outPattern), null);
- setMetadataExtractionPatterns(patterns);
- setIgnoredLineConsumer(IGNORED_LINE_LOGGER);
- } catch (Exception e) {
- throw new TikaConfigException(e.getMessage(), e);
- }
- }
-
- @Override
- public void checkInitialization(InitializableProblemHandler handler)
- throws TikaConfigException {
- //TODO -- what do we want to check?
- }
-
- @Override
- public List recognise(InputStream stream, ContentHandler handler,
- Metadata metadata, ParseContext context)
- throws IOException, SAXException, TikaException {
- Metadata md = new Metadata();
- parse(stream, handler, md, context);
- List objects = new ArrayList<>();
- for (String key : md.names()) {
- double confidence = Double.parseDouble(md.get(key));
- objects.add(new RecognisedObject(key, "eng", key, confidence));
- }
- return objects;
- }
-}
-
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/tf/TensorflowRESTRecogniser.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/tf/TensorflowRESTRecogniser.java
deleted file mode 100644
index fa07ef4e259..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/tf/TensorflowRESTRecogniser.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tika.parser.recognition.tf;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import com.github.openjson.JSONArray;
-import com.github.openjson.JSONObject;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import org.apache.tika.config.Field;
-import org.apache.tika.config.InitializableProblemHandler;
-import org.apache.tika.config.Param;
-import org.apache.tika.exception.TikaConfigException;
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.mime.MediaType;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.parser.recognition.ObjectRecogniser;
-import org.apache.tika.parser.recognition.RecognisedObject;
-
-/**
- * Tensor Flow image recogniser which has high performance.
- * This implementation uses Tensorflow via REST API.
- *
- * NOTE : https://wiki.apache.org/tika/TikaAndVision
- *
- * @since Apache Tika 1.14
- */
-public class TensorflowRESTRecogniser implements ObjectRecogniser {
-
- protected static final String LABEL_LANG = "eng";
- /**
- * Some variables are protected, because this class is extended by
- * TensorflowRESTVideoRecognizer class
- */
-
- private static final Logger LOG = LoggerFactory.getLogger(TensorflowRESTRecogniser.class);
- private static final Set SUPPORTED_MIMES = Collections.unmodifiableSet(new HashSet<>(
- Arrays.asList(new MediaType[]{MediaType.image("jpeg"), MediaType.image("png"),
- MediaType.image("gif")})));
- @Field
- protected URI apiBaseUri = URI.create("http://localhost:8764/inception/v4");
-
- @Field
- protected int topN = 2;
-
- @Field
- protected double minConfidence = 0.015;
-
- protected URI apiUri;
-
- protected URI healthUri;
-
- protected boolean available;
-
- protected URI getApiUri(Metadata metadata) {
- return apiUri;
- }
-
- @Override
- public Set getSupportedMimes() {
- return SUPPORTED_MIMES;
- }
-
- @Override
- public boolean isAvailable() {
- return available;
- }
-
- @Override
- public void initialize(Map params) throws TikaConfigException {
- healthUri = URI.create(apiBaseUri + "/ping");
- apiUri = URI.create(apiBaseUri + String.format(Locale.getDefault(),
- "/classify/image?topn=%1$d&min_confidence=%2$f", topN, minConfidence));
-
- try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
- HttpResponse response = client.execute(new HttpGet(healthUri));
- available = response.getStatusLine().getStatusCode() == 200;
-
- LOG.info("Available = {}, API Status = {}", available, response.getStatusLine());
- LOG.info("topN = {}, minConfidence = {}", topN, minConfidence);
- } catch (Exception e) {
- available = false;
- throw new TikaConfigException(e.getMessage(), e);
- }
- }
-
- @Override
- public void checkInitialization(InitializableProblemHandler handler)
- throws TikaConfigException {
- //TODO -- what do we want to check?
- }
-
- @Override
- public List recognise(InputStream stream, ContentHandler handler,
- Metadata metadata, ParseContext context)
- throws IOException, SAXException, TikaException {
- List recObjs = new ArrayList<>();
- try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
-
- HttpPost request = new HttpPost(getApiUri(metadata));
-
- try (UnsynchronizedByteArrayOutputStream byteStream = UnsynchronizedByteArrayOutputStream.builder().get()) {
- //TODO: convert this to stream, this might cause OOM issue
- // InputStreamEntity is not working
- // request.setEntity(new InputStreamEntity(stream, -1));
- IOUtils.copy(stream, byteStream);
- request.setEntity(new ByteArrayEntity(byteStream.toByteArray()));
- }
-
- HttpResponse response = client.execute(request);
- try (InputStream reply = response.getEntity().getContent()) {
- String replyMessage = IOUtils.toString(reply, StandardCharsets.UTF_8);
- if (response.getStatusLine().getStatusCode() == 200) {
- JSONObject jReply = new JSONObject(replyMessage);
- JSONArray jClasses = jReply.getJSONArray("classnames");
- JSONArray jConfidence = jReply.getJSONArray("confidence");
- if (jClasses.length() != jConfidence.length()) {
- LOG.warn("Classes of size {} is not equal to confidence of size {}",
- jClasses.length(), jConfidence.length());
- }
- assert jClasses.length() == jConfidence.length();
- for (int i = 0; i < jClasses.length(); i++) {
- RecognisedObject recObj =
- new RecognisedObject(jClasses.getString(i), LABEL_LANG,
- jClasses.getString(i), jConfidence.getDouble(i));
- recObjs.add(recObj);
- }
- } else {
- LOG.warn("Status = {}", response.getStatusLine());
- LOG.warn("Response = {}", replyMessage);
- }
- }
- } catch (Exception e) {
- LOG.warn(e.getMessage(), e);
- }
- LOG.debug("Num Objects found {}", recObjs.size());
- return recObjs;
- }
-}
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/tf/TensorflowRESTVideoRecogniser.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/tf/TensorflowRESTVideoRecogniser.java
deleted file mode 100644
index 4094bc416a7..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/java/org/apache/tika/parser/recognition/tf/TensorflowRESTVideoRecogniser.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tika.parser.recognition.tf;
-
-import java.net.URI;
-import java.util.Collections;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import jakarta.ws.rs.core.UriBuilder;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.tika.config.Field;
-import org.apache.tika.config.Param;
-import org.apache.tika.config.TikaConfig;
-import org.apache.tika.exception.TikaConfigException;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.mime.MediaType;
-import org.apache.tika.mime.MimeType;
-import org.apache.tika.mime.MimeTypeException;
-
-/**
- * Tensor Flow video recogniser which has high performance.
- * This implementation uses Tensorflow via REST API.
- *
- * NOTE : https://wiki.apache.org/tika/TikaAndVisionVideo
- *
- * @since Apache Tika 1.15
- */
-public class TensorflowRESTVideoRecogniser extends TensorflowRESTRecogniser {
-
- private static final Logger LOG = LoggerFactory.getLogger(TensorflowRESTVideoRecogniser.class);
-
- private static final Set SUPPORTED_MIMES =
- Collections.singleton(MediaType.video("mp4"));
-
- @Field
- private String mode = "fixed";
-
- @Override
- protected URI getApiUri(Metadata metadata) {
- TikaConfig config = TikaConfig.getDefaultConfig();
- String ext = null;
- //Find extension for video. It's required for OpenCV in InceptionAPI to decode video
- try {
- MimeType mimeType = config.getMimeRepository().forName(metadata.get("Content-Type"));
- ext = mimeType.getExtension();
- return UriBuilder.fromUri(apiUri).queryParam("ext", ext).build();
- } catch (MimeTypeException e) {
- LOG.error("Can't find extension from metadata");
- return apiUri;
- }
- }
-
- @Override
- public void initialize(Map params) throws TikaConfigException {
- healthUri = URI.create(apiBaseUri + "/ping");
- apiUri = URI.create(apiBaseUri + String.format(Locale.getDefault(),
- "/classify/video?topn=%1$d&min_confidence=%2$f&mode=%3$s", topN, minConfidence,
- mode));
-
- try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
- HttpResponse response = client.execute(new HttpGet(healthUri));
- available = response.getStatusLine().getStatusCode() == 200;
-
- LOG.info("Available = {}, API Status = {}", available, response.getStatusLine());
- LOG.info("topN = {}, minConfidence = {}", topN, minConfidence);
- } catch (Exception e) {
- available = false;
- throw new TikaConfigException(e.getMessage(), e);
- }
- }
-}
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/caption_generator.py b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/caption_generator.py
deleted file mode 100644
index 9dc13e4b695..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/caption_generator.py
+++ /dev/null
@@ -1,179 +0,0 @@
-#!/usr/bin/env python
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import heapq
-import math
-import numpy as np
-
-
-class Caption(object):
- """
- A complete or partial caption object
- """
-
- def __init__(self, sentence, state, logprob, score):
- """Initializes the Caption"""
-
- # list of word_ids in the caption
- self.sentence = sentence
- # model state after generating the previous word
- self.state = state
- # log probability of the caption
- self.logprob = logprob
- # score of the caption
- self.score = score
-
- def __cmp__(self, other):
- """Compares Captions by score"""
-
- assert isinstance(other, Caption)
- if self.score == other.score:
- return 0
- elif self.score < other.score:
- return -1
- else:
- return 1
-
- # for Python 3 compatibility (__cmp__ is deprecated).
- def __lt__(self, other):
- assert isinstance(other, Caption)
- return self.score < other.score
-
- # also for Python 3 compatibility.
- def __eq__(self, other):
- assert isinstance(other, Caption)
- return self.score == other.score
-
-
-class TopN(object):
- """Maintains the top N elements of an incrementally provided set"""
-
- def __init__(self, n):
- self._n = n
- self._data = []
-
- def size(self):
- assert self._data is not None
- return len(self._data)
-
- def push(self, x):
- """Pushes a new element"""
-
- assert self._data is not None
- if len(self._data) < self._n:
- heapq.heappush(self._data, x)
- else:
- heapq.heappushpop(self._data, x)
-
- def extract(self, sort=False):
- """
- Extracts all elements from the TopN. This is a destructive operation,
- The only method that can be called immediately after extract() is reset()
- """
- assert self._data is not None
- data = self._data
- self._data = None
- if sort:
- data.sort(reverse=True)
- return data
-
- def reset(self):
- """Returns the TopN to an empty state"""
-
- self._data = []
-
-
-class CaptionGenerator(object):
- """
- Class to generate captions from an image-to-text model
- """
-
- def __init__(self,
- model,
- vocab,
- beam_size,
- max_caption_length,
- length_normalization_factor=0.0):
-
- self.vocab = vocab
- self.model = model
-
- self.beam_size = beam_size
- self.max_caption_length = max_caption_length
- self.length_normalization_factor = length_normalization_factor
-
- def beam_search(self, sess, encoded_image):
- """Runs beam search caption generation on a single image"""
-
- # feed in the image to get the initial state.
- initial_state = self.model.feed_image(sess, encoded_image)
-
- initial_beam = Caption(
- sentence=[self.vocab.start_id],
- state=initial_state[0],
- logprob=0.0,
- score=0.0)
- partial_captions = TopN(self.beam_size)
- partial_captions.push(initial_beam)
- complete_captions = TopN(self.beam_size)
-
- # run beam search.
- for _ in range(self.max_caption_length - 1):
- partial_captions_list = partial_captions.extract()
- partial_captions.reset()
- input_feed = np.array([c.sentence[-1] for c in partial_captions_list])
- state_feed = np.array([c.state for c in partial_captions_list])
-
- softmax, new_states = self.model.inference_step(sess, input_feed, state_feed)
-
- for i, partial_caption in enumerate(partial_captions_list):
- word_probabilities = softmax[i]
- state = new_states[i]
- # for this partial caption, get the beam_size most probable next words.
- words_and_probs = list(enumerate(word_probabilities))
- words_and_probs.sort(key=lambda x: -x[1])
- words_and_probs = words_and_probs[0:self.beam_size]
- # each next word gives a new partial caption.
- for w, p in words_and_probs:
- if p < 1e-12:
- continue # avoid log(0).
- sentence = partial_caption.sentence + [w]
- logprob = partial_caption.logprob + math.log(p)
- score = logprob
-
- if w == self.vocab.end_id:
- if self.length_normalization_factor > 0:
- score /= len(sentence) ** self.length_normalization_factor
- beam = Caption(sentence, state, logprob, score)
- complete_captions.push(beam)
- else:
- beam = Caption(sentence, state, logprob, score)
- partial_captions.push(beam)
- if partial_captions.size() == 0:
- # we have run out of partial candidates; happens when beam_size = 1.
- break
-
- # if we have no complete captions then fall back to the partial captions,
- # but never output a mixture of complete and partial captions because a
- # partial caption could have a higher score than all the complete captions
- if not complete_captions.size():
- complete_captions = partial_captions
-
- return complete_captions.extract(sort=True)
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/im2txtapi.py b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/im2txtapi.py
deleted file mode 100644
index 97f1f2afd64..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/im2txtapi.py
+++ /dev/null
@@ -1,266 +0,0 @@
-#!/usr/bin/env python
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-
-"""
- This script exposes image captioning service over a REST API. Image captioning implementation based on the paper,
-
- "Show and Tell: A Neural Image Caption Generator"
- Oriol Vinyals, Alexander Toshev, Samy Bengio, Dumitru Erhan
-
- For more details, please visit :
- http://arxiv.org/abs/1411.4555
- Requirements :
- Flask
- tensorflow
- numpy
- requests
-"""
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import json
-import logging
-import math
-import requests
-import sys
-
-from flask import Flask, request, Response, jsonify
-from io import BytesIO
-from PIL import Image
-from time import time
-
-import tensorflow as tf
-import xml.etree.ElementTree as ET
-
-import model_wrapper
-import vocabulary
-import caption_generator
-
-# turning off the traceback by limiting its depth
-sys.tracebacklimit = 0
-
-# informative log messages for advanced users to troubleshoot errors when modifying model_info.xml
-try:
- info = ET.parse('/usr/share/apache-tika/models/dl/image/caption/model_info.xml').getroot()
-except IOError:
- logging.exception('model_info.xml is not found')
- sys.exit(1)
-
-model_main = info.find('model_main')
-if model_main is None:
- logging.exception(' tag under tag in model_info.xml is not found')
- sys.exit(1)
-
-checkpoint_path = model_main.find('checkpoint_path')
-if checkpoint_path is None:
- logging.exception(' tag under tag in model_info.xml is not found')
- sys.exit(1)
-else:
- checkpoint_path = checkpoint_path.text
-
-vocab_file = model_main.find('vocab_file')
-if vocab_file is None:
- logging.exception(' tag under tag in model_info.xml is not found')
- sys.exit(1)
-else:
- vocab_file = vocab_file.text
-
-port = info.get('port')
-if port is None:
- logging.exception('port attribute in tag in model_info.xml is not found')
- sys.exit(1)
-
-# turning on the traceback by setting it to default
-sys.tracebacklimit = 1000
-
-FLAGS = tf.flags.FLAGS
-tf.flags.DEFINE_string("checkpoint_path", checkpoint_path, """Directory containing the model checkpoint file.""")
-tf.flags.DEFINE_string('vocab_file', vocab_file, """Text file containing the vocabulary.""")
-tf.flags.DEFINE_integer('port', port, """Server PORT, default:8764""")
-
-tf.logging.set_verbosity(tf.logging.INFO)
-
-
-class Initializer(Flask):
- """
- Class to initialize the REST API, this class loads the model from the given checkpoint path in model_info.xml
- and prepares a caption_generator object
- """
-
- def __init__(self, name):
- super(Initializer, self).__init__(name)
- # build the inference graph
- g = tf.Graph()
- with g.as_default():
- model = model_wrapper.ModelWrapper()
- restore_fn = model.build_graph(FLAGS.checkpoint_path)
- g.finalize()
- # make the model globally available
- self.model = model
- # create the vocabulary
- self.vocab = vocabulary.Vocabulary(FLAGS.vocab_file)
- self.sess = tf.Session(graph=g)
- # load the model from checkpoint
- restore_fn(self.sess)
-
-
-def current_time():
- """Returns current time in milli seconds"""
-
- return int(1000 * time())
-
-
-app = Initializer(__name__)
-
-
-def get_remote_file(url, success=200, timeout=10):
- """
- Given HTTP URL, this api gets the content of it
- returns (Content-Type, image_content)
- """
- try:
- app.logger.info("GET: %s" % url)
- auth = None
- res = requests.get(url, stream=True, timeout=timeout, auth=auth)
- if res.status_code == success:
- return res.headers.get('Content-Type', 'application/octet-stream'), res.raw.data
- except:
- pass
- return None, None
-
-
-@app.route("/")
-def index():
- """The index page which provide information about other API end points"""
-
- return """
-
-
Image Captioning REST API
-
The following API end points are valid
-
- Inception V3
- -
/inception/v3/ping -
- Description : checks availability of the service. returns "pong" with status 200 when it is available
-
- -
/inception/v3/caption/image -
-
- | Description | This is a service that can caption images |
- | How to supply Image Content |
- | With HTTP GET : |
- Include a query parameter url which is an http url of JPEG image
- Example: curl "localhost:8764/inception/v3/caption/image?url=http://xyz.com/example.jpg"
- |
- | With HTTP POST : |
- POST JPEG image content as binary data in request body.
- Example: curl -X POST "localhost:8764/inception/v3/caption/image" --data-binary @example.jpg
- |
-
-
-
- """
-
-
-@app.route("/inception/v3/ping", methods=["GET"])
-def ping_pong():
- """API to do health check. If this says status code 200, then healthy"""
-
- return "pong"
-
-
-@app.route("/inception/v3/caption/image", methods=["GET", "POST"])
-def caption_image():
- """API to caption images"""
- image_format = "not jpeg"
-
- st = current_time()
- # get beam_size
- beam_size = int(request.args.get("beam_size", "3"))
- # get max_caption_length
- max_caption_length = int(request.args.get("max_caption_length", "20"))
- # get image_data
- if request.method == 'POST':
- image_data = request.get_data()
- else:
- url = request.args.get("url")
- c_type, image_data = get_remote_file(url)
- if not image_data:
- return Response(status=400, response=jsonify(error="Could not HTTP GET %s" % url))
- if 'image/jpeg' in c_type:
- image_format = "jpeg"
-
- # use c_type to find whether image_format is jpeg or not
- # if jpeg, don't convert
- if image_format == "jpeg":
- jpg_image = image_data
- # if not jpeg
- else:
- # open the image from raw bytes
- image = Image.open(BytesIO(image_data))
- # convert the image to RGB format, otherwise will give errors when converting to jpeg, if the image isn't RGB
- rgb_image = image.convert("RGB")
- # convert the RGB image to jpeg
- image_bytes = BytesIO()
- rgb_image.save(image_bytes, format="jpeg", quality=95)
- jpg_image = image_bytes.getvalue()
- image_bytes.close()
-
- read_time = current_time() - st
- # restart counter
- st = current_time()
-
- generator = caption_generator.CaptionGenerator(app.model,
- app.vocab,
- beam_size=beam_size,
- max_caption_length=max_caption_length)
- captions = generator.beam_search(app.sess, jpg_image)
-
- captioning_time = current_time() - st
- app.logger.info("Captioning time : %d" % captioning_time)
-
- array_captions = []
- for caption in captions:
- sentence = [app.vocab.id_to_word(w) for w in caption.sentence[1:-1]]
- sentence = " ".join(sentence)
- array_captions.append({
- 'sentence': sentence,
- 'confidence': math.exp(caption.logprob)
- })
-
- response = {
- 'beam_size': beam_size,
- 'max_caption_length': max_caption_length,
- 'captions': array_captions,
- 'time': {
- 'read': read_time,
- 'captioning': captioning_time,
- 'units': 'ms'
- }
- }
- return Response(response=json.dumps(response), status=200, mimetype="application/json")
-
-
-def main(_):
- if not app.debug:
- print("Serving on port %d" % FLAGS.port)
- app.run(host="0.0.0.0", port=FLAGS.port)
-
-
-if __name__ == '__main__':
- tf.app.run()
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/model_info.xml b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/model_info.xml
deleted file mode 100644
index da838170288..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/model_info.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
- /usr/share/apache-tika/models/dl/image/caption/1M_iters_ckpt/model.ckpt-1000000
-
- /usr/share/apache-tika/models/dl/image/caption/1M_iters_ckpt/word_counts.txt
-
-
\ No newline at end of file
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/model_wrapper.py b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/model_wrapper.py
deleted file mode 100644
index a542e7b9a6f..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/model_wrapper.py
+++ /dev/null
@@ -1,340 +0,0 @@
-#!/usr/bin/env python
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import os.path
-
-import tensorflow as tf
-from tensorflow.contrib.slim.python.slim.nets.inception_v3 import inception_v3_base
-
-slim = tf.contrib.slim
-
-
-class ModelWrapper(object):
- """
- Model wrapper class to perform image captioning with a ShowAndTellModel
- """
-
- def __init__(self):
- super(ModelWrapper, self).__init__()
-
- def build_graph(self, checkpoint_path):
- """Builds the inference graph"""
-
- tf.logging.info("Building model.")
- ShowAndTellModel().build()
- saver = tf.train.Saver()
-
- return self._create_restore_fn(checkpoint_path, saver)
-
- def _create_restore_fn(self, checkpoint_path, saver):
- """Creates a function that restores a model from checkpoint file"""
-
- if tf.gfile.IsDirectory(checkpoint_path):
- checkpoint_path = tf.train.latest_checkpoint(checkpoint_path)
- if not checkpoint_path:
- raise ValueError("No checkpoint file found in: %s" % checkpoint_path)
-
- def _restore_fn(sess):
- tf.logging.info("Loading model from checkpoint: %s", checkpoint_path)
- saver.restore(sess, checkpoint_path)
- tf.logging.info("Successfully loaded checkpoint: %s",
- os.path.basename(checkpoint_path))
-
- return _restore_fn
-
- def feed_image(self, sess, encoded_image):
- initial_state = sess.run(fetches="lstm/initial_state:0",
- feed_dict={"image_feed:0": encoded_image})
- return initial_state
-
- def inference_step(self, sess, input_feed, state_feed):
- softmax_output, state_output = sess.run(
- fetches=["softmax:0", "lstm/state:0"],
- feed_dict={
- "input_feed:0": input_feed,
- "lstm/state_feed:0": state_feed,
- })
- return softmax_output, state_output
-
-
-class ShowAndTellModel(object):
- """
- Image captioning implementation based on the paper,
-
- "Show and Tell: A Neural Image Caption Generator"
- Oriol Vinyals, Alexander Toshev, Samy Bengio, Dumitru Erhan
-
- For more details, please visit : http://arxiv.org/abs/1411.4555
- """
-
- def __init__(self):
-
- # scale used to initialize model variables
- self.initializer_scale = 0.08
-
- # dimensions of Inception v3 input images
- self.image_height = 299
- self.image_width = 299
-
- # LSTM input and output dimensionality, respectively
- self.embedding_size = 512
- self.num_lstm_units = 512
-
- # number of unique words in the vocab (plus 1, for )
- # the default value is larger than the expected actual vocab size to allow
- # for differences between tokenizer versions used in preprocessing, there is
- # no harm in using a value greater than the actual vocab size, but using a
- # value less than the actual vocab size will result in an error
- self.vocab_size = 12000
-
- # reader for the input data
- self.reader = tf.TFRecordReader()
-
- # to match the "Show and Tell" paper we initialize all variables with a
- # random uniform initializer
- self.initializer = tf.random_uniform_initializer(
- minval=-self.initializer_scale,
- maxval=self.initializer_scale)
-
- # a float32 Tensor with shape [batch_size, height, width, channels]
- self.images = None
-
- # an int32 Tensor with shape [batch_size, padded_length]
- self.input_seqs = None
-
- # an int32 Tensor with shape [batch_size, padded_length]
- self.target_seqs = None
-
- # an int32 0/1 Tensor with shape [batch_size, padded_length]
- self.input_mask = None
-
- # a float32 Tensor with shape [batch_size, embedding_size]
- self.image_embeddings = None
-
- # a float32 Tensor with shape [batch_size, padded_length, embedding_size]
- self.seq_embeddings = None
-
- # collection of variables from the inception submodel
- self.inception_variables = []
-
- # global step Tensor
- self.global_step = None
-
- def process_image(self, encoded_image, resize_height=346, resize_width=346, thread_id=0):
- """Decodes and processes an image string"""
-
- # helper function to log an image summary to the visualizer. Summaries are
- # only logged in thread 0
- def image_summary(name, img):
- if not thread_id:
- tf.summary.image(name, tf.expand_dims(img, 0))
-
- # decode image into a float32 Tensor of shape [?, ?, 3] with values in [0, 1)
- with tf.name_scope("decode", values=[encoded_image]):
- image = tf.image.decode_jpeg(encoded_image, channels=3)
-
- image = tf.image.convert_image_dtype(image, dtype=tf.float32)
- image_summary("original_image", image)
-
- # resize image
- assert (resize_height > 0) == (resize_width > 0)
- if resize_height:
- image = tf.image.resize_images(image,
- size=[resize_height, resize_width],
- method=tf.image.ResizeMethod.BILINEAR)
-
- # central crop, assuming resize_height > height, resize_width > width
- image = tf.image.resize_image_with_crop_or_pad(image, self.image_height, self.image_width)
-
- image_summary("resized_image", image)
-
- image_summary("final_image", image)
-
- # rescale to [-1,1] instead of [0, 1]
- image = tf.subtract(image, 0.5)
- image = tf.multiply(image, 2.0)
- return image
-
- def build_inputs(self):
- """Input prefetching, preprocessing and batching"""
-
- image_feed = tf.placeholder(dtype=tf.string, shape=[], name="image_feed")
- input_feed = tf.placeholder(dtype=tf.int64,
- shape=[None], # batch_size
- name="input_feed")
-
- # process image and insert batch dimensions
- images = tf.expand_dims(self.process_image(image_feed), 0)
- input_seqs = tf.expand_dims(input_feed, 1)
-
- # no target sequences or input mask in inference mode
- target_seqs = None
- input_mask = None
-
- self.images = images
- self.input_seqs = input_seqs
- self.target_seqs = target_seqs
- self.input_mask = input_mask
-
- def build_image_embeddings(self):
- """Builds the image model(Inception V3) subgraph and generates image embeddings"""
-
- # parameter initialization
- batch_norm_params = {
- "is_training": False,
- "trainable": False,
- # decay for the moving averages
- "decay": 0.9997,
- # epsilon to prevent 0s in variance
- "epsilon": 0.001,
- # collection containing the moving mean and moving variance
- "variables_collections": {
- "beta": None,
- "gamma": None,
- "moving_mean": ["moving_vars"],
- "moving_variance": ["moving_vars"],
- }
- }
-
- stddev = 0.1,
- dropout_keep_prob = 0.8
-
- with tf.variable_scope("InceptionV3", "InceptionV3", [self.images]) as scope:
- with slim.arg_scope(
- [slim.conv2d, slim.fully_connected],
- weights_regularizer=None,
- trainable=False):
- with slim.arg_scope(
- [slim.conv2d],
- weights_initializer=tf.truncated_normal_initializer(stddev=stddev),
- activation_fn=tf.nn.relu,
- normalizer_fn=slim.batch_norm,
- normalizer_params=batch_norm_params):
- net, end_points = inception_v3_base(self.images, scope=scope)
- with tf.variable_scope("logits"):
- shape = net.get_shape()
- net = slim.avg_pool2d(net, shape[1:3], padding="VALID", scope="pool")
- net = slim.dropout(
- net,
- keep_prob=dropout_keep_prob,
- is_training=False,
- scope="dropout")
- net = slim.flatten(net, scope="flatten")
-
- # add summaries
- for v in end_points.values():
- tf.contrib.layers.summaries.summarize_activation(v)
-
- self.inception_variables = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope="InceptionV3")
-
- # map inception output(net) into embedding space
- with tf.variable_scope("image_embedding") as scope:
- image_embeddings = tf.contrib.layers.fully_connected(
- inputs=net,
- num_outputs=self.embedding_size,
- activation_fn=None,
- weights_initializer=self.initializer,
- biases_initializer=None,
- scope=scope)
-
- # save the embedding size in the graph
- tf.constant(self.embedding_size, name="embedding_size")
-
- self.image_embeddings = image_embeddings
-
- def build_seq_embeddings(self):
- """Builds the input sequence embeddings"""
-
- with tf.variable_scope("seq_embedding"), tf.device("/cpu:0"):
- embedding_map = tf.get_variable(
- name="map",
- shape=[self.vocab_size, self.embedding_size],
- initializer=self.initializer)
- seq_embeddings = tf.nn.embedding_lookup(embedding_map, self.input_seqs)
-
- self.seq_embeddings = seq_embeddings
-
- def build_model(self):
-
- # this LSTM cell has biases and outputs tanh(new_c) * sigmoid(o), but the
- # modified LSTM in the "Show and Tell" paper has no biases and outputs
- # new_c * sigmoid(o).
-
- lstm_cell = tf.contrib.rnn.BasicLSTMCell(
- num_units=self.num_lstm_units, state_is_tuple=True)
-
- with tf.variable_scope("lstm", initializer=self.initializer) as lstm_scope:
- # feed the image embeddings to set the initial LSTM state
- zero_state = lstm_cell.zero_state(
- batch_size=self.image_embeddings.get_shape()[0], dtype=tf.float32)
- _, initial_state = lstm_cell(self.image_embeddings, zero_state)
-
- # allow the LSTM variables to be reused
- lstm_scope.reuse_variables()
-
- # because this is inference mode,
- # use concatenated states for convenient feeding and fetching
- tf.concat(axis=1, values=initial_state, name="initial_state")
-
- # placeholder for feeding a batch of concatenated states
- state_feed = tf.placeholder(dtype=tf.float32,
- shape=[None, sum(lstm_cell.state_size)],
- name="state_feed")
- state_tuple = tf.split(value=state_feed, num_or_size_splits=2, axis=1)
-
- # run a single LSTM step
- lstm_outputs, state_tuple = lstm_cell(
- inputs=tf.squeeze(self.seq_embeddings, axis=[1]),
- state=state_tuple)
-
- # concatentate the resulting state
- tf.concat(axis=1, values=state_tuple, name="state")
-
- # stack batches vertically
- lstm_outputs = tf.reshape(lstm_outputs, [-1, lstm_cell.output_size])
-
- with tf.variable_scope("logits") as logits_scope:
- logits = tf.contrib.layers.fully_connected(
- inputs=lstm_outputs,
- num_outputs=self.vocab_size,
- activation_fn=None,
- weights_initializer=self.initializer,
- scope=logits_scope)
-
- tf.nn.softmax(logits, name="softmax")
-
- def setup_global_step(self):
- """Sets up the global step Tensor"""
-
- global_step = tf.Variable(
- initial_value=0,
- name="global_step",
- trainable=False,
- collections=[tf.GraphKeys.GLOBAL_STEP, tf.GraphKeys.GLOBAL_VARIABLES])
-
- self.global_step = global_step
-
- def build(self):
- self.build_inputs()
- self.build_image_embeddings()
- self.build_seq_embeddings()
- self.build_model()
- self.setup_global_step()
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/vocabulary.py b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/vocabulary.py
deleted file mode 100644
index 566b034cb0a..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/captioning/tf/vocabulary.py
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/env python
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import tensorflow as tf
-
-
-class Vocabulary(object):
- """
- Vocabulary class for an image-to-text model
- """
-
- def __init__(self,
- vocab_file,
- start_word="",
- end_word="",
- unk_word=""):
- """Initializes the vocabulary"""
-
- if not tf.gfile.Exists(vocab_file):
- tf.logging.fatal("Vocab file %s not found.", vocab_file)
- tf.logging.info("Initializing vocabulary from file: %s", vocab_file)
-
- with tf.gfile.GFile(vocab_file, mode="r") as f:
- reverse_vocab = list(f.readlines())
- reverse_vocab = [line.split()[0] for line in reverse_vocab]
- assert start_word in reverse_vocab
- assert end_word in reverse_vocab
- if unk_word not in reverse_vocab:
- reverse_vocab.append(unk_word)
- vocab = dict([(x, y) for (y, x) in enumerate(reverse_vocab)])
-
- tf.logging.info("Created vocabulary with %d words" % len(vocab))
-
- # vocab[word] = id
- self.vocab = vocab
- # reverse_vocab[id] = word
- self.reverse_vocab = reverse_vocab
-
- # save special word ids
- self.start_id = vocab[start_word]
- self.end_id = vocab[end_word]
- self.unk_id = vocab[unk_word]
-
- def id_to_word(self, word_id):
- """Returns the word string of an integer word id"""
-
- if word_id >= len(self.reverse_vocab):
- return self.reverse_vocab[self.unk_id]
- else:
- return self.reverse_vocab[word_id]
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tf/inception_v4.py b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tf/inception_v4.py
deleted file mode 100644
index b6644160fed..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tf/inception_v4.py
+++ /dev/null
@@ -1,362 +0,0 @@
-#!/usr/bin/env python
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-"""Contains the definition of the Inception V4 architecture.
-
-As described in http://arxiv.org/abs/1602.07261.
-
- Inception-v4, Inception-ResNet and the Impact of Residual Connections
- on Learning
- Christian Szegedy, Sergey Ioffe, Vincent Vanhoucke, Alex Alemi
-"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import tensorflow as tf
-
-slim = tf.contrib.slim
-
-
-def block_inception_a(inputs, scope=None, reuse=None):
- """Builds Inception-A block for Inception v4 network."""
- # By default use stride=1 and SAME padding
- with slim.arg_scope([slim.conv2d, slim.avg_pool2d, slim.max_pool2d],
- stride=1, padding='SAME'):
- with tf.variable_scope(scope, 'BlockInceptionA', [inputs], reuse=reuse):
- with tf.variable_scope('Branch_0'):
- branch_0 = slim.conv2d(inputs, 96, [1, 1], scope='Conv2d_0a_1x1')
- with tf.variable_scope('Branch_1'):
- branch_1 = slim.conv2d(inputs, 64, [1, 1], scope='Conv2d_0a_1x1')
- branch_1 = slim.conv2d(branch_1, 96, [3, 3], scope='Conv2d_0b_3x3')
- with tf.variable_scope('Branch_2'):
- branch_2 = slim.conv2d(inputs, 64, [1, 1], scope='Conv2d_0a_1x1')
- branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0b_3x3')
- branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0c_3x3')
- with tf.variable_scope('Branch_3'):
- branch_3 = slim.avg_pool2d(inputs, [3, 3], scope='AvgPool_0a_3x3')
- branch_3 = slim.conv2d(branch_3, 96, [1, 1], scope='Conv2d_0b_1x1')
- return tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
-
-
-def block_reduction_a(inputs, scope=None, reuse=None):
- """Builds Reduction-A block for Inception v4 network."""
- # By default use stride=1 and SAME padding
- with slim.arg_scope([slim.conv2d, slim.avg_pool2d, slim.max_pool2d],
- stride=1, padding='SAME'):
- with tf.variable_scope(scope, 'BlockReductionA', [inputs], reuse=reuse):
- with tf.variable_scope('Branch_0'):
- branch_0 = slim.conv2d(inputs, 384, [3, 3], stride=2, padding='VALID',
- scope='Conv2d_1a_3x3')
- with tf.variable_scope('Branch_1'):
- branch_1 = slim.conv2d(inputs, 192, [1, 1], scope='Conv2d_0a_1x1')
- branch_1 = slim.conv2d(branch_1, 224, [3, 3], scope='Conv2d_0b_3x3')
- branch_1 = slim.conv2d(branch_1, 256, [3, 3], stride=2,
- padding='VALID', scope='Conv2d_1a_3x3')
- with tf.variable_scope('Branch_2'):
- branch_2 = slim.max_pool2d(inputs, [3, 3], stride=2, padding='VALID',
- scope='MaxPool_1a_3x3')
- return tf.concat(axis=3, values=[branch_0, branch_1, branch_2])
-
-
-def block_inception_b(inputs, scope=None, reuse=None):
- """Builds Inception-B block for Inception v4 network."""
- # By default use stride=1 and SAME padding
- with slim.arg_scope([slim.conv2d, slim.avg_pool2d, slim.max_pool2d],
- stride=1, padding='SAME'):
- with tf.variable_scope(scope, 'BlockInceptionB', [inputs], reuse=reuse):
- with tf.variable_scope('Branch_0'):
- branch_0 = slim.conv2d(inputs, 384, [1, 1], scope='Conv2d_0a_1x1')
- with tf.variable_scope('Branch_1'):
- branch_1 = slim.conv2d(inputs, 192, [1, 1], scope='Conv2d_0a_1x1')
- branch_1 = slim.conv2d(branch_1, 224, [1, 7], scope='Conv2d_0b_1x7')
- branch_1 = slim.conv2d(branch_1, 256, [7, 1], scope='Conv2d_0c_7x1')
- with tf.variable_scope('Branch_2'):
- branch_2 = slim.conv2d(inputs, 192, [1, 1], scope='Conv2d_0a_1x1')
- branch_2 = slim.conv2d(branch_2, 192, [7, 1], scope='Conv2d_0b_7x1')
- branch_2 = slim.conv2d(branch_2, 224, [1, 7], scope='Conv2d_0c_1x7')
- branch_2 = slim.conv2d(branch_2, 224, [7, 1], scope='Conv2d_0d_7x1')
- branch_2 = slim.conv2d(branch_2, 256, [1, 7], scope='Conv2d_0e_1x7')
- with tf.variable_scope('Branch_3'):
- branch_3 = slim.avg_pool2d(inputs, [3, 3], scope='AvgPool_0a_3x3')
- branch_3 = slim.conv2d(branch_3, 128, [1, 1], scope='Conv2d_0b_1x1')
- return tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
-
-
-def block_reduction_b(inputs, scope=None, reuse=None):
- """Builds Reduction-B block for Inception v4 network."""
- # By default use stride=1 and SAME padding
- with slim.arg_scope([slim.conv2d, slim.avg_pool2d, slim.max_pool2d],
- stride=1, padding='SAME'):
- with tf.variable_scope(scope, 'BlockReductionB', [inputs], reuse=reuse):
- with tf.variable_scope('Branch_0'):
- branch_0 = slim.conv2d(inputs, 192, [1, 1], scope='Conv2d_0a_1x1')
- branch_0 = slim.conv2d(branch_0, 192, [3, 3], stride=2,
- padding='VALID', scope='Conv2d_1a_3x3')
- with tf.variable_scope('Branch_1'):
- branch_1 = slim.conv2d(inputs, 256, [1, 1], scope='Conv2d_0a_1x1')
- branch_1 = slim.conv2d(branch_1, 256, [1, 7], scope='Conv2d_0b_1x7')
- branch_1 = slim.conv2d(branch_1, 320, [7, 1], scope='Conv2d_0c_7x1')
- branch_1 = slim.conv2d(branch_1, 320, [3, 3], stride=2,
- padding='VALID', scope='Conv2d_1a_3x3')
- with tf.variable_scope('Branch_2'):
- branch_2 = slim.max_pool2d(inputs, [3, 3], stride=2, padding='VALID',
- scope='MaxPool_1a_3x3')
- return tf.concat(axis=3, values=[branch_0, branch_1, branch_2])
-
-
-def block_inception_c(inputs, scope=None, reuse=None):
- """Builds Inception-C block for Inception v4 network."""
- # By default use stride=1 and SAME padding
- with slim.arg_scope([slim.conv2d, slim.avg_pool2d, slim.max_pool2d],
- stride=1, padding='SAME'):
- with tf.variable_scope(scope, 'BlockInceptionC', [inputs], reuse=reuse):
- with tf.variable_scope('Branch_0'):
- branch_0 = slim.conv2d(inputs, 256, [1, 1], scope='Conv2d_0a_1x1')
- with tf.variable_scope('Branch_1'):
- branch_1 = slim.conv2d(inputs, 384, [1, 1], scope='Conv2d_0a_1x1')
- branch_1 = tf.concat(axis=3, values=[
- slim.conv2d(branch_1, 256, [1, 3], scope='Conv2d_0b_1x3'),
- slim.conv2d(branch_1, 256, [3, 1], scope='Conv2d_0c_3x1')])
- with tf.variable_scope('Branch_2'):
- branch_2 = slim.conv2d(inputs, 384, [1, 1], scope='Conv2d_0a_1x1')
- branch_2 = slim.conv2d(branch_2, 448, [3, 1], scope='Conv2d_0b_3x1')
- branch_2 = slim.conv2d(branch_2, 512, [1, 3], scope='Conv2d_0c_1x3')
- branch_2 = tf.concat(axis=3, values=[
- slim.conv2d(branch_2, 256, [1, 3], scope='Conv2d_0d_1x3'),
- slim.conv2d(branch_2, 256, [3, 1], scope='Conv2d_0e_3x1')])
- with tf.variable_scope('Branch_3'):
- branch_3 = slim.avg_pool2d(inputs, [3, 3], scope='AvgPool_0a_3x3')
- branch_3 = slim.conv2d(branch_3, 256, [1, 1], scope='Conv2d_0b_1x1')
- return tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
-
-
-def inception_v4_base(inputs, final_endpoint='Mixed_7d', scope=None):
- """Creates the Inception V4 network up to the given final endpoint.
-
- Args:
- inputs: a 4-D tensor of size [batch_size, height, width, 3].
- final_endpoint: specifies the endpoint to construct the network up to.
- It can be one of [ 'Conv2d_1a_3x3', 'Conv2d_2a_3x3', 'Conv2d_2b_3x3',
- 'Mixed_3a', 'Mixed_4a', 'Mixed_5a', 'Mixed_5b', 'Mixed_5c', 'Mixed_5d',
- 'Mixed_5e', 'Mixed_6a', 'Mixed_6b', 'Mixed_6c', 'Mixed_6d', 'Mixed_6e',
- 'Mixed_6f', 'Mixed_6g', 'Mixed_6h', 'Mixed_7a', 'Mixed_7b', 'Mixed_7c',
- 'Mixed_7d']
- scope: Optional variable_scope.
-
- Returns:
- logits: the logits outputs of the model.
- end_points: the set of end_points from the inception model.
-
- Raises:
- ValueError: if final_endpoint is not set to one of the predefined values,
- """
- end_points = {}
-
- def add_and_check_final(name, net):
- end_points[name] = net
- return name == final_endpoint
-
- with tf.variable_scope(scope, 'InceptionV4', [inputs]):
- with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],
- stride=1, padding='SAME'):
- # 299 x 299 x 3
- net = slim.conv2d(inputs, 32, [3, 3], stride=2,
- padding='VALID', scope='Conv2d_1a_3x3')
- if add_and_check_final('Conv2d_1a_3x3', net): return net, end_points
- # 149 x 149 x 32
- net = slim.conv2d(net, 32, [3, 3], padding='VALID',
- scope='Conv2d_2a_3x3')
- if add_and_check_final('Conv2d_2a_3x3', net): return net, end_points
- # 147 x 147 x 32
- net = slim.conv2d(net, 64, [3, 3], scope='Conv2d_2b_3x3')
- if add_and_check_final('Conv2d_2b_3x3', net): return net, end_points
- # 147 x 147 x 64
- with tf.variable_scope('Mixed_3a'):
- with tf.variable_scope('Branch_0'):
- branch_0 = slim.max_pool2d(net, [3, 3], stride=2, padding='VALID',
- scope='MaxPool_0a_3x3')
- with tf.variable_scope('Branch_1'):
- branch_1 = slim.conv2d(net, 96, [3, 3], stride=2, padding='VALID',
- scope='Conv2d_0a_3x3')
- net = tf.concat(axis=3, values=[branch_0, branch_1])
- if add_and_check_final('Mixed_3a', net): return net, end_points
-
- # 73 x 73 x 160
- with tf.variable_scope('Mixed_4a'):
- with tf.variable_scope('Branch_0'):
- branch_0 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
- branch_0 = slim.conv2d(branch_0, 96, [3, 3], padding='VALID',
- scope='Conv2d_1a_3x3')
- with tf.variable_scope('Branch_1'):
- branch_1 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
- branch_1 = slim.conv2d(branch_1, 64, [1, 7], scope='Conv2d_0b_1x7')
- branch_1 = slim.conv2d(branch_1, 64, [7, 1], scope='Conv2d_0c_7x1')
- branch_1 = slim.conv2d(branch_1, 96, [3, 3], padding='VALID',
- scope='Conv2d_1a_3x3')
- net = tf.concat(axis=3, values=[branch_0, branch_1])
- if add_and_check_final('Mixed_4a', net): return net, end_points
-
- # 71 x 71 x 192
- with tf.variable_scope('Mixed_5a'):
- with tf.variable_scope('Branch_0'):
- branch_0 = slim.conv2d(net, 192, [3, 3], stride=2, padding='VALID',
- scope='Conv2d_1a_3x3')
- with tf.variable_scope('Branch_1'):
- branch_1 = slim.max_pool2d(net, [3, 3], stride=2, padding='VALID',
- scope='MaxPool_1a_3x3')
- net = tf.concat(axis=3, values=[branch_0, branch_1])
- if add_and_check_final('Mixed_5a', net): return net, end_points
-
- # 35 x 35 x 384
- # 4 x Inception-A blocks
- for idx in range(4):
- block_scope = 'Mixed_5' + chr(ord('b') + idx)
- net = block_inception_a(net, block_scope)
- if add_and_check_final(block_scope, net): return net, end_points
-
- # 35 x 35 x 384
- # Reduction-A block
- net = block_reduction_a(net, 'Mixed_6a')
- if add_and_check_final('Mixed_6a', net): return net, end_points
-
- # 17 x 17 x 1024
- # 7 x Inception-B blocks
- for idx in range(7):
- block_scope = 'Mixed_6' + chr(ord('b') + idx)
- net = block_inception_b(net, block_scope)
- if add_and_check_final(block_scope, net): return net, end_points
-
- # 17 x 17 x 1024
- # Reduction-B block
- net = block_reduction_b(net, 'Mixed_7a')
- if add_and_check_final('Mixed_7a', net): return net, end_points
-
- # 8 x 8 x 1536
- # 3 x Inception-C blocks
- for idx in range(3):
- block_scope = 'Mixed_7' + chr(ord('b') + idx)
- net = block_inception_c(net, block_scope)
- if add_and_check_final(block_scope, net): return net, end_points
- raise ValueError('Unknown final endpoint %s' % final_endpoint)
-
-
-def inception_v4(inputs, num_classes=1001, is_training=True,
- dropout_keep_prob=0.8,
- reuse=None,
- scope='InceptionV4',
- create_aux_logits=True):
- """Creates the Inception V4 model.
-
- Args:
- inputs: a 4-D tensor of size [batch_size, height, width, 3].
- num_classes: number of predicted classes.
- is_training: whether is training or not.
- dropout_keep_prob: float, the fraction to keep before final layer.
- reuse: whether or not the network and its variables should be reused. To be
- able to reuse 'scope' must be given.
- scope: Optional variable_scope.
- create_aux_logits: Whether to include the auxiliary logits.
-
- Returns:
- logits: the logits outputs of the model.
- end_points: the set of end_points from the inception model.
- """
- end_points = {}
- with tf.variable_scope(scope, 'InceptionV4', [inputs], reuse=reuse) as scope:
- with slim.arg_scope([slim.batch_norm, slim.dropout],
- is_training=is_training):
- net, end_points = inception_v4_base(inputs, scope=scope)
-
- with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],
- stride=1, padding='SAME'):
- # Auxiliary Head logits
- if create_aux_logits:
- with tf.variable_scope('AuxLogits'):
- # 17 x 17 x 1024
- aux_logits = end_points['Mixed_6h']
- aux_logits = slim.avg_pool2d(aux_logits, [5, 5], stride=3,
- padding='VALID',
- scope='AvgPool_1a_5x5')
- aux_logits = slim.conv2d(aux_logits, 128, [1, 1],
- scope='Conv2d_1b_1x1')
- aux_logits = slim.conv2d(aux_logits, 768,
- aux_logits.get_shape()[1:3],
- padding='VALID', scope='Conv2d_2a')
- aux_logits = slim.flatten(aux_logits)
- aux_logits = slim.fully_connected(aux_logits, num_classes,
- activation_fn=None,
- scope='Aux_logits')
- end_points['AuxLogits'] = aux_logits
-
- # Final pooling and prediction
- with tf.variable_scope('Logits'):
- # 8 x 8 x 1536
- net = slim.avg_pool2d(net, net.get_shape()[1:3], padding='VALID',
- scope='AvgPool_1a')
- # 1 x 1 x 1536
- net = slim.dropout(net, dropout_keep_prob, scope='Dropout_1b')
- net = slim.flatten(net, scope='PreLogitsFlatten')
- end_points['PreLogitsFlatten'] = net
- # 1536
- logits = slim.fully_connected(net, num_classes, activation_fn=None,
- scope='Logits')
- end_points['Logits'] = logits
- end_points['Predictions'] = tf.nn.softmax(logits, name='Predictions')
- return logits, end_points
-
-
-def inception_v4_arg_scope(weight_decay=0.00004,
- use_batch_norm=True,
- batch_norm_decay=0.9997,
- batch_norm_epsilon=0.001):
- """Defines the default arg scope for inception models.
- Args:
- weight_decay: The weight decay to use for regularizing the model.
- use_batch_norm: "If `True`, batch_norm is applied after each convolution.
- batch_norm_decay: Decay for batch norm moving average.
- batch_norm_epsilon: Small float added to variance to avoid dividing by zero
- in batch norm.
- Returns:
- An `arg_scope` to use for the inception models.
- """
- batch_norm_params = {
- # Decay for the moving averages.
- 'decay': batch_norm_decay,
- # epsilon to prevent 0s in variance.
- 'epsilon': batch_norm_epsilon,
- # collection containing update_ops.
- 'updates_collections': tf.GraphKeys.UPDATE_OPS,
- }
- if use_batch_norm:
- normalizer_fn = slim.batch_norm
- normalizer_params = batch_norm_params
- else:
- normalizer_fn = None
- normalizer_params = {}
- # Set weight_decay for weights in Conv and FC layers.
- with slim.arg_scope([slim.conv2d, slim.fully_connected],
- weights_regularizer=slim.l2_regularizer(weight_decay)):
- with slim.arg_scope(
- [slim.conv2d],
- weights_initializer=slim.variance_scaling_initializer(),
- activation_fn=tf.nn.relu,
- normalizer_fn=normalizer_fn,
- normalizer_params=normalizer_params) as sc:
- return sc
-
-
-default_image_size = 299
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tf/inceptionapi.py b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tf/inceptionapi.py
deleted file mode 100755
index 09d830c0ddc..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tf/inceptionapi.py
+++ /dev/null
@@ -1,483 +0,0 @@
-#!/usr/bin/env python
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-"""
- Image classification with Inception.
-
- This script exposes the tensorflow's inception classification service over REST API.
-
- For more details, visit:
- https://tensorflow.org/tutorials/image_recognition/
-
- Requirements :
- Flask
- tensorflow
- numpy
- requests
- pillow
-"""
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import os
-import tempfile
-import json
-import logging
-import requests
-
-from flask import Flask, request, Response, jsonify
-from io import BytesIO
-from logging.handlers import RotatingFileHandler
-from PIL import Image
-from time import time
-
-import tensorflow as tf
-
-from inception_v4 import default_image_size, inception_v4_arg_scope, inception_v4
-
-try:
- # This import is placed inside here to ensure that video_util and OpenCV is not required for image recognition APIs
- from video_util import get_center_frame, get_frames_interval, get_n_frames
-except:
- print("Can't import video libraries, No video functionality is available")
-
-json.encoder.FLOAT_REPR = lambda o: format(o, '.2f') # JSON serialization of floats
-slim = tf.contrib.slim
-FLAGS = tf.app.flags.FLAGS
-
-tf.app.flags.DEFINE_string('model_dir',
- '/usr/share/apache-tika/models/dl/image-video/recognition/',
- """Path to inception_v4.ckpt & meta files""")
-tf.app.flags.DEFINE_integer('port',
- '8764',
- """Server PORT, default:8764""")
-tf.app.flags.DEFINE_string('log',
- 'inception.log',
- """Log file name, default: inception.log""")
-
-
-def preprocess_image(image, height, width, central_fraction=0.875, scope=None):
- """Prepare one image for evaluation.
- If height and width are specified it would output an image with that size by
- applying resize_bilinear.
- If central_fraction is specified it would crop the central fraction of the
- input image.
- Args:
- image: 3-D Tensor of image. If dtype is tf.float32 then the range should be
- [0, 1], otherwise it would converted to tf.float32 assuming that the range
- is [0, MAX], where MAX is largest positive representable number for
- int(8/16/32) data type (see `tf.image.convert_image_dtype` for details).
- height: integer
- width: integer
- central_fraction: Optional Float, fraction of the image to crop.
- scope: Optional scope for name_scope.
- Returns:
- 3-D float Tensor of prepared image.
- """
- with tf.name_scope(scope, 'eval_image', [image, height, width]):
- if image.dtype != tf.float32:
- image = tf.image.convert_image_dtype(image, dtype=tf.float32)
- # Crop the central region of the image with an area containing 87.5% of
- # the original image.
- if central_fraction:
- image = tf.image.central_crop(image, central_fraction=central_fraction)
-
- if height and width:
- # Resize the image to the specified height and width.
- image = tf.expand_dims(image, 0)
- image = tf.image.resize_bilinear(image, [height, width],
- align_corners=False)
- image = tf.squeeze(image, [0])
- image = tf.subtract(image, 0.5)
- image = tf.multiply(image, 2.0)
- return image
-
-
-def create_readable_names_for_imagenet_labels():
- """
- Create a dict mapping label id to human readable string.
- Returns:
- labels_to_names: dictionary where keys are integers from to 1000
- and values are human-readable names.
-
- We retrieve a synset file, which contains a list of valid synset labels used
- by ILSVRC competition. There is one synset one per line, eg.
- # n01440764
- # n01443537
- We also retrieve a synset_to_human_file, which contains a mapping from synsets
- to human-readable names for every synset in Imagenet. These are stored in a
- tsv format, as follows:
- # n02119247 black fox
- # n02119359 silver fox
- We assign each synset (in alphabetical order) an integer, starting from 1
- (since 0 is reserved for the background class).
-
- Code is based on
- https://github.com/tensorflow/models/blob/master/inception/inception/data/build_imagenet_data.py
- """
-
- dest_directory = FLAGS.model_dir
-
- synset_list = [s.strip() for s in open(os.path.join(dest_directory, 'imagenet_lsvrc_2015_synsets.txt')).readlines()]
- num_synsets_in_ilsvrc = len(synset_list)
- assert num_synsets_in_ilsvrc == 1000
-
- synset_to_human_list = open(os.path.join(dest_directory, 'imagenet_metadata.txt')).readlines()
- num_synsets_in_all_imagenet = len(synset_to_human_list)
- assert num_synsets_in_all_imagenet == 21842
-
- synset_to_human = {}
- for s in synset_to_human_list:
- parts = s.strip().split('\t')
- assert len(parts) == 2
- synset = parts[0]
- human = parts[1]
- synset_to_human[synset] = human
-
- label_index = 1
- labels_to_names = {0: 'background'}
- for synset in synset_list:
- name = synset_to_human[synset]
- labels_to_names[label_index] = name
- label_index += 1
-
- return labels_to_names
-
-
-def get_remote_file(url, success=200, timeout=10):
- """
- Given HTTP URL, this api gets the content of it
- returns (Content-Type, image_content)
- """
- try:
- app.logger.info("GET: %s" % url)
- auth = None
- res = requests.get(url, stream=True, timeout=timeout, auth=auth)
- if res.status_code == success:
- return res.headers.get('Content-Type', 'application/octet-stream'), res.raw.data
- except:
- pass
- return None, None
-
-
-def current_time():
- """Returns current time in milli seconds"""
-
- return int(1000 * time())
-
-
-class Classifier(Flask):
- """Classifier Service class"""
-
- def __init__(self, name):
- super(Classifier, self).__init__(name)
- file_handler = RotatingFileHandler(FLAGS.log, maxBytes=1024 * 1024 * 100, backupCount=20)
- file_handler.setLevel(logging.INFO)
- formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
- file_handler.setFormatter(formatter)
- self.logger.addHandler(file_handler)
- self.names = create_readable_names_for_imagenet_labels()
- self.image_size = default_image_size
-
- self.image_str_placeholder = tf.placeholder(tf.string)
- image = tf.image.decode_jpeg(self.image_str_placeholder, channels=3)
- processed_image = preprocess_image(image, self.image_size, self.image_size)
- processed_images = tf.expand_dims(processed_image, 0)
- # create the model, use the default arg scope to configure the batch norm parameters.
- with slim.arg_scope(inception_v4_arg_scope()):
- logits, _ = inception_v4(processed_images, num_classes=1001, is_training=False)
- self.probabilities = tf.nn.softmax(logits)
-
- dest_directory = FLAGS.model_dir
- init_fn = slim.assign_from_checkpoint_fn(
- os.path.join(dest_directory, 'inception_v4.ckpt'),
- slim.get_model_variables('InceptionV4'))
-
- self.sess = tf.Session()
- init_fn(self.sess)
-
- def classify(self, image_string, topn, min_confidence):
- eval_probabilities = self.sess.run(self.probabilities, feed_dict={self.image_str_placeholder: image_string})
- eval_probabilities = eval_probabilities[0, 0:]
- sorted_inds = [i[0] for i in sorted(enumerate(-eval_probabilities), key=lambda x: x[1])]
-
- if topn is None:
- topn = len(sorted_inds)
-
- res = []
- for i in range(topn):
- index = sorted_inds[i]
- score = float(eval_probabilities[index])
- if min_confidence is None:
- res.append((index, self.names[index], score))
- else:
- if score >= min_confidence:
- res.append((index, self.names[index], score))
- else:
- # the scores are in sorted order, so we can break the loop whenever we get a low score object
- break
- return res
-
-
-app = Classifier(__name__)
-
-
-@app.route("/")
-def index():
- """The index page which provide information about other API end points"""
-
- return """
-
-
Inception REST API
-
The following API end points are valid
-
- Inception V4
- -
/inception/v4/ping -
- Description : checks availability of the service. returns "pong" with status 200 when it is available
-
- -
/inception/v4/classify/image -
-
- | Description | This is a classifier service that can classify images |
- | Query Params :
- topn : type = int : top classes to get; default : 5
- min_confidence : type = float : minimum confidence that a label should have to exist in topn; default : 0.015
- human : type = boolean : human readable class names; default : true
- |
- | How to supply Image Content |
- | With HTTP GET : |
- Include a query parameter url which is an http url of JPEG image
- Example: curl "localhost:8764/inception/v4/classify/image?url=http://xyz.com/example.jpg"
- |
- | With HTTP POST : |
- POST JPEG image content as binary data in request body.
- Example: curl -X POST "localhost:8764/inception/v4/classify/image?topn=5&min_confidence=0.015&human=false" --data-binary @example.jpg
- |
-
-
- -
/inception/v4/classify/video -
-
- | Description | This is a classifier service that can classify videos |
- | Query Params :
- topn : type = int : top classes to get; default : 5
- min_confidence : type = float : minimum confidence that a label should have to exist in topn; default : 0.015
- human : type = boolean : human readable class names; default : true
- mode : options = {"center", "interval", "fixed"} : Modes of frame extraction; default : center
- "center" - Just one frame in center.
- "interval" - Extracts frames after fixed interval.
- "fixed" - Extract fixed number of frames.
- frame-interval : type = int : Interval for frame extraction to be used with INTERVAL mode. If frame_interval=10 then every 10th frame will be extracted; default : 10
- num-frame : type = int : Number of frames to be extracted from video while using FIXED model. If num_frame=10 then 10 frames equally distant from each other will be extracted; default : 10
-
- |
- | How to supply Video Content |
- | With HTTP GET : |
- Include a query parameter url which is path on file system
- Example: curl "localhost:8764/inception/v4/classify/video?url=filesystem/path/to/video"
- |
|---|
- | With HTTP POST : |
- POST video content as binary data in request body. If video can be decoded by OpenCV it should be fine. It's tested on mp4 and avi on mac
- Include a query parameter ext this extension is needed to tell OpenCV which decoder to use, default is ".mp4"
- Example: curl -X POST "localhost:8764/inception/v4/classify/video?topn=5&min_confidence=0.015&human=false" --data-binary @example.mp4
- |
-
-
-
- """
-
-
-@app.route("/inception/v4/ping", methods=["GET"])
-def ping_pong():
- """API to do health check. If this says status code 200, then healthy"""
-
- return "pong"
-
-
-@app.route("/inception/v4/classify/image", methods=["GET", "POST"])
-def classify_image():
- """API to classify images"""
-
- image_format = "not jpeg"
-
- st = current_time()
- topn = int(request.args.get("topn", "5"))
- min_confidence = float(request.args.get("min_confidence", "0.015"))
- human = request.args.get("human", "true").lower() in ("true", "1", "yes")
- if request.method == 'POST':
- image_data = request.get_data()
- else:
- url = request.args.get("url")
- c_type, image_data = get_remote_file(url)
- if not image_data:
- return Response(status=400, response=jsonify(error="Could not HTTP GET %s" % url))
- if 'image/jpeg' in c_type:
- image_format = "jpeg"
-
- # use c_type to find whether image_format is jpeg or not
- # if jpeg, don't convert
- if image_format == "jpeg":
- jpg_image = image_data
- # if not jpeg
- else:
- # open the image from raw bytes
- image = Image.open(BytesIO(image_data))
- # convert the image to RGB format, otherwise will give errors when converting to jpeg, if the image isn't RGB
- rgb_image = image.convert("RGB")
- # convert the RGB image to jpeg
- image_bytes = BytesIO()
- rgb_image.save(image_bytes, format="jpeg", quality=95)
- jpg_image = image_bytes.getvalue()
- image_bytes.close()
-
- read_time = current_time() - st
- st = current_time() # reset start time
- try:
- classes = app.classify(image_string=jpg_image, topn=topn, min_confidence=min_confidence)
- except Exception as e:
- app.logger.error(e)
- return Response(status=400, response=str(e))
- classids, classnames, confidence = zip(*classes)
-
- print(classnames, confidence)
-
- classifier_time = current_time() - st
- app.logger.info("Classifier time : %d" % classifier_time)
- res = {
- 'classids': classids,
- 'confidence': confidence,
- 'time': {
- 'read': read_time,
- 'classification': classifier_time,
- 'units': 'ms'
- }
- }
- if human:
- res['classnames'] = classnames
- return Response(response=json.dumps(res), status=200, mimetype="application/json")
-
-
-@app.route("/inception/v4/classify/video", methods=["GET", "POST"])
-def classify_video():
- """
- API to classify videos
- Request args -
- url - PATH of file
- topn - number of top scoring labels
- min_confidence - minimum confidence that a label should have to exist in topn
- human - human readable or not
- mode - Modes of frame extraction {"center", "interval", "fixed"}
- "center" - Just one frame in center.
- "interval" - Extracts frames after fixed interval.
- "fixed" - Extract fixed number of frames.
- frame-interval - Interval for frame extraction to be used with INTERVAL mode. If frame_interval=10 then every 10th frame will be extracted.
- num-frame - Number of frames to be extracted from video while using FIXED model. If num_frame=10 then 10 frames equally distant from each other will be extracted
-
- ext - If video is sent in binary format, then ext is needed to tell OpenCV which decoder to use. eg ".mp4"
- """
-
- st = current_time()
- topn = int(request.args.get("topn", "5"))
- min_confidence = float(request.args.get("min_confidence", "0.015"))
- human = request.args.get("human", "true").lower() in ("true", "1", "yes")
-
- mode = request.args.get("mode", "center").lower()
- if mode not in {"center", "interval", "fixed"}:
- '''
- Throw invalid request error
- '''
- return Response(status=400, response=jsonify(error="not a valid mode. Available mode %s" % str(ALLOWED_MODE)))
-
- frame_interval = int(request.args.get("frame-interval", "10"))
- num_frame = int(request.args.get("num-frame", "10"))
-
- if request.method == 'POST':
- video_data = request.get_data()
- ext = request.args.get("ext", ".mp4").lower()
-
- temp_file = tempfile.NamedTemporaryFile(suffix=ext)
- temp_file.file.write(video_data)
- temp_file.file.close()
-
- url = temp_file.name
- else:
- url = request.args.get("url")
-
- read_time = current_time() - st
- st = current_time() # reset start time
-
- if mode == "center":
- image_data_arr = [get_center_frame(url)]
- elif mode == "interval":
- image_data_arr = get_frames_interval(url, frame_interval)
- else:
- image_data_arr = get_n_frames(url, num_frame)
-
- classes = []
- for image_data in image_data_arr:
- try:
- _classes = app.classify(image_data, topn=None, min_confidence=None)
- except Exception as e:
- app.logger.error(e)
- return Response(status=400, response=str(e))
-
- _classes.sort()
- if len(classes) == 0:
- classes = _classes
- else:
- for idx, _c in enumerate(_classes):
- c = list(classes[idx])
- c[2] += _c[2]
- classes[idx] = tuple(c)
-
- top_classes = []
- for c in classes:
- c = list(c)
- # avg out confidence score
- avg_score = c[2] / len(image_data_arr)
- c[2] = avg_score
- if avg_score >= min_confidence:
- top_classes.append(tuple(c))
-
- top_classes = sorted(top_classes, key=lambda tup: tup[2])[-topn:][::-1]
-
- classids, classnames, confidence = zip(*top_classes)
-
- classifier_time = current_time() - st
- app.logger.info("Classifier time : %d" % classifier_time)
- res = {
- 'classids': classids,
- 'confidence': confidence,
- 'time': {
- 'read': read_time,
- 'classification': classifier_time,
- 'units': 'ms'
- }
- }
- if human:
- res['classnames'] = classnames
- return Response(response=json.dumps(res), status=200, mimetype="application/json")
-
-
-def main(_):
- if not app.debug:
- print("Serving on port %d" % FLAGS.port)
- app.run(host="0.0.0.0", port=FLAGS.port)
-
-
-if __name__ == '__main__':
- tf.app.run()
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tf/video_util.py b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tf/video_util.py
deleted file mode 100644
index a4c208bf9ca..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tf/video_util.py
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/usr/bin/env python
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import cv2
-import ntpath
-import numpy as np
-
-print("cv2.__version__", cv2.__version__)
-
-CV_FRAME_COUNT = None
-
-if hasattr(cv2, "cv"):
- CV_FRAME_COUNT = cv2.cv.CV_CAP_PROP_FRAME_COUNT
-else:
- CV_FRAME_COUNT = cv2.CAP_PROP_FRAME_COUNT
-
-
-def _get_image_from_array(image_array):
- # JPG to support tensorflow
- byte_arr = cv2.imencode(".jpg", image_array)[1]
- return "".join(map(chr, byte_arr))
-
-
-def _path_leaf(path):
- """
- Returns file name from path. Path should not end with slash(/)
- """
- head, tail = ntpath.split(path)
- return tail or ntpath.basename(head)
-
-
-def get_center_frame(video_path):
- """
- Traverse till half of video and saves center snapshot
- @param video_path: Path to video file on system
- """
- cap = cv2.VideoCapture(video_path)
-
- length = int(cap.get(CV_FRAME_COUNT))
-
- success, image = cap.read()
- count = 0
-
- while success and count < length / 2:
- success, image = cap.read()
- count += 1
-
- return _get_image_from_array(image)
-
-
-def get_frames_interval(video_path, frame_interval):
- """
- Selects one frames after every frame_interval
- @param video_path: Path to video file on system
- @param frame_interval: Interval after which frame should be picked. If frame_interval=10 then every 10th frame will be extracted
- """
- cap = cv2.VideoCapture(video_path)
-
- length = int(cap.get(CV_FRAME_COUNT))
-
- success, image = cap.read()
- count = 0
-
- image_arr = []
- while success and count < length:
- success, image = cap.read()
- if count % frame_interval == 0:
- image = _get_image_from_array(image)
- image_arr.append(image)
-
- count += 1
-
- return image_arr
-
-
-def get_n_frames(video_path, num_frame):
- """
- Get N frames equidistant to each other in a video
- @param video_path: Path to video file on system
- @param num_frame: Number of frames to be extracted from video. If num_frame=10 then 10 frames equally distant from each other will be extracted
- """
- cap = cv2.VideoCapture(video_path)
-
- length = int(cap.get(CV_FRAME_COUNT))
-
- op_frame_idx = set(np.linspace(0, length - 2, num_frame, dtype=int))
-
- success, image = cap.read()
- count = 0
-
- image_arr = []
- while success and count < length:
- success, image = cap.read()
- if success and count in op_frame_idx:
- image = _get_image_from_array(image)
- image_arr.append(image)
-
- count += 1
-
- return image_arr
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow-im2txt-rest.xml b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow-im2txt-rest.xml
deleted file mode 100644
index ce85f7b9860..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow-im2txt-rest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
- image/jpeg
- image/png
- image/gif
-
- http://localhost:8764/inception/v3
- 5
- 15
- org.apache.tika.parser.captioning.tf.TensorflowRESTCaptioner
-
-
-
-
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow-rest.xml b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow-rest.xml
deleted file mode 100644
index 69a65d0765a..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow-rest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
- image/jpeg
- image/png
- image/gif
-
- http://localhost:8764/inception/v4
- 2
- 0.015
- org.apache.tika.parser.recognition.tf.TensorflowRESTRecogniser
-
-
-
-
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow-video-rest.xml b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow-video-rest.xml
deleted file mode 100644
index 3096745402d..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow-video-rest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
- video/mp4
-
- http://localhost:8764/inception/v4
- 2
- 0.015
- fixed
- org.apache.tika.parser.recognition.tf.TensorflowRESTVideoRecogniser
-
-
-
-
-
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow.xml b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow.xml
deleted file mode 100644
index f848d15dca1..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/main/resources/org/apache/tika/parser/recognition/tika-config-tflow.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
- image/jpeg
-
- 2
- 0.015
- org.apache.tika.parser.recognition.tf.TensorflowImageRecParser
-
-
-
-
\ No newline at end of file
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/test/java/org/apache/tika/parser/recognition/ObjectRecognitionParserTest.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/test/java/org/apache/tika/parser/recognition/ObjectRecognitionParserTest.java
deleted file mode 100644
index 272a0daffb6..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/test/java/org/apache/tika/parser/recognition/ObjectRecognitionParserTest.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tika.parser.recognition;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-import java.util.List;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.cxf.jaxrs.client.WebClient;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xml.sax.SAXException;
-
-import org.apache.tika.Tika;
-import org.apache.tika.config.TikaConfig;
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.parser.recognition.tf.TensorflowImageRecParser;
-
-/**
- * Testcases for Object Recognition Parser
- */
-public class ObjectRecognitionParserTest {
-
- // Config files
- private static final String CONFIG_FILE_OBJ_REC =
- "org/apache/tika/parser/recognition/tika-config-tflow.xml";
- private static final String CONFIG_REST_FILE_OBJ_REC =
- "org/apache/tika/parser/recognition/tika-config-tflow-rest.xml";
- private static final String CONFIG_REST_FILE_IM2TXT =
- "org/apache/tika/parser/recognition/tika-config-tflow-im2txt-rest.xml";
-
- // Test images
- private static final String CAT_IMAGE_JPEG = "test-documents/testJPEG.jpg";
- private static final String CAT_IMAGE_PNG = "test-documents/testPNG.png";
- private static final String CAT_IMAGE_GIF = "test-documents/testGIF.gif";
-
- private static final String BASEBALL_IMAGE_JPEG = "test-documents/baseball.jpg";
- private static final String BASEBALL_IMAGE_PNG = "test-documents/baseball.png";
- private static final String BASEBALL_IMAGE_GIF = "test-documents/baseball.gif";
-
- private static final ClassLoader loader = ObjectRecognitionParserTest.class.getClassLoader();
-
- private static final Logger LOG = LoggerFactory.getLogger(ObjectRecognitionParserTest.class);
-
- @Test
- public void jpegTFObjRecTest() throws IOException, TikaException, SAXException {
- TensorflowImageRecParser p = new TensorflowImageRecParser();
- assumeTrue(p.isAvailable());
- try (InputStream stream = loader.getResourceAsStream(CONFIG_FILE_OBJ_REC)) {
- assert stream != null;
- Tika tika = new Tika(new TikaConfig(stream));
- Metadata metadata = new Metadata();
- try (InputStream imageStream = loader.getResourceAsStream(CAT_IMAGE_JPEG)) {
- Reader reader = tika.parse(imageStream, metadata);
- List lines = IOUtils.readLines(reader);
- String text = StringUtils.join(lines, " ");
- String[] expectedObjects = {"Egyptian cat", "tabby, tabby cat"};
- String metaValues = StringUtils
- .join(metadata.getValues(ObjectRecognitionParser.MD_KEY_OBJ_REC), " ");
- for (String expectedObject : expectedObjects) {
- String message = "'" + expectedObject + "' must have been detected";
- assertTrue(text.contains(expectedObject), message);
- assertTrue(metaValues.contains(expectedObject), message);
- }
- }
- }
- }
-
- @Test
- public void jpegRESTObjRecTest() throws Exception {
- String apiUrl = "http://localhost:8764/inception/v4/ping";
- boolean available = false;
- int status = 500;
- try {
- status = WebClient.create(apiUrl).get().getStatus();
- available = status == 200;
- } catch (Exception ignore) {
- }
- assumeTrue(available);
- String[] expectedObjects = {"Egyptian cat", "tabby, tabby cat"};
- doRecognize(CONFIG_REST_FILE_OBJ_REC, CAT_IMAGE_JPEG,
- ObjectRecognitionParser.MD_KEY_OBJ_REC, expectedObjects);
- }
-
- @Test
- public void pngRESTObjRecTest() throws Exception {
- String apiUrl = "http://localhost:8764/inception/v4/ping";
- boolean available = false;
- int status = 500;
- try {
- status = WebClient.create(apiUrl).get().getStatus();
- available = status == 200;
- } catch (Exception ignore) {
- }
- assumeTrue(available);
- String[] expectedObjects = {"Egyptian cat", "tabby, tabby cat"};
- doRecognize(CONFIG_REST_FILE_OBJ_REC, CAT_IMAGE_PNG, ObjectRecognitionParser.MD_KEY_OBJ_REC,
- expectedObjects);
- }
-
- @Test
- public void gifRESTObjRecTest() throws Exception {
- String apiUrl = "http://localhost:8764/inception/v4/ping";
- boolean available = false;
- int status = 500;
- try {
- status = WebClient.create(apiUrl).get().getStatus();
- available = status == 200;
- } catch (Exception ignore) {
- }
- assumeTrue(available);
- String[] expectedObjects = {"Egyptian cat"};
- doRecognize(CONFIG_REST_FILE_OBJ_REC, CAT_IMAGE_GIF, ObjectRecognitionParser.MD_KEY_OBJ_REC,
- expectedObjects);
- }
-
- @Test
- public void jpegRESTim2txtTest() throws Exception {
- String apiUrl = "http://localhost:8764/inception/v3/ping";
- boolean available = false;
- int status = 500;
- try {
- status = WebClient.create(apiUrl).get().getStatus();
- available = status == 200;
- } catch (Exception ignore) {
- }
- assumeTrue(available);
- String[] expectedCaption = {"a baseball player holding a bat on a field"};
- doRecognize(CONFIG_REST_FILE_IM2TXT, BASEBALL_IMAGE_JPEG,
- ObjectRecognitionParser.MD_KEY_IMG_CAP, expectedCaption);
- }
-
- @Test
- public void pngRESTim2txtTest() throws Exception {
- String apiUrl = "http://localhost:8764/inception/v3/ping";
- boolean available = false;
- int status = 500;
- try {
- status = WebClient.create(apiUrl).get().getStatus();
- available = status == 200;
- } catch (Exception ignore) {
- }
- assumeTrue(available);
- String[] expectedCaption = {"a baseball player holding a bat on a field"};
- doRecognize(CONFIG_REST_FILE_IM2TXT, BASEBALL_IMAGE_PNG,
- ObjectRecognitionParser.MD_KEY_IMG_CAP, expectedCaption);
- }
-
- @Test
- public void gifRESTim2txtTest() throws Exception {
- String apiUrl = "http://localhost:8764/inception/v3/ping";
- boolean available = false;
- int status = 500;
- try {
- status = WebClient.create(apiUrl).get().getStatus();
- available = status == 200;
- } catch (Exception ignore) {
- }
- assumeTrue(available);
- String[] expectedCaption = {"a baseball player pitching a ball on top of a field"};
- doRecognize(CONFIG_REST_FILE_IM2TXT, BASEBALL_IMAGE_GIF,
- ObjectRecognitionParser.MD_KEY_IMG_CAP, expectedCaption);
- }
-
- private void doRecognize(String configFile, String testImg, String mdKey,
- String[] expectedObjects) throws Exception {
- try (InputStream stream = loader.getResourceAsStream(configFile)) {
- assert stream != null;
- Tika tika = new Tika(new TikaConfig(stream));
- Metadata metadata = new Metadata();
- try (InputStream imageStream = loader.getResourceAsStream(testImg)) {
- Reader reader = tika.parse(imageStream, metadata);
- String text = IOUtils.toString(reader);
- String metaValues = StringUtils.join(metadata.getValues(mdKey), " ");
- LOG.info("MetaValues = {}", metaValues);
- for (String expectedObject : expectedObjects) {
- String message = "'" + expectedObject + "' must have been detected";
- assertTrue(text.contains(expectedObject), message);
- assertTrue(metaValues.contains(expectedObject), message);
- }
- }
- }
- }
-}
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/test/java/org/apache/tika/parser/recognition/tf/TensorflowImageRecParserTest.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/test/java/org/apache/tika/parser/recognition/tf/TensorflowImageRecParserTest.java
deleted file mode 100644
index 28e5b6fe02a..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/test/java/org/apache/tika/parser/recognition/tf/TensorflowImageRecParserTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tika.parser.recognition.tf;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.xml.sax.helpers.DefaultHandler;
-
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.parser.recognition.RecognisedObject;
-
-
-@Disabled
-public class TensorflowImageRecParserTest {
-
- @Test
- public void recognise() throws Exception {
- TensorflowImageRecParser recogniser = new TensorflowImageRecParser();
- recogniser.initialize(new HashMap<>());
- try (InputStream stream = getClass().getClassLoader()
- .getResourceAsStream("test-documents/testJPEG.jpg")) {
- List objects = recogniser
- .recognise(stream, new DefaultHandler(), new Metadata(), new ParseContext());
- assertTrue(5 == objects.size());
- Set objectLabels = new HashSet<>();
- for (RecognisedObject object : objects) {
- objectLabels.add(object.getLabel());
- }
- System.out.println(objectLabels);
- String[] expected = {"Egyptian cat", "tabby, tabby cat"};
- for (String label : expected) {
- assertTrue(objectLabels.contains(label), label + " is expected");
- }
- }
- }
-}
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/test/java/org/apache/tika/parser/recognition/tf/TensorflowVideoRecParserTest.java b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/test/java/org/apache/tika/parser/recognition/tf/TensorflowVideoRecParserTest.java
deleted file mode 100644
index c77f9f7bb60..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-module/src/test/java/org/apache/tika/parser/recognition/tf/TensorflowVideoRecParserTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tika.parser.recognition.tf;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.xml.sax.helpers.DefaultHandler;
-
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.parser.recognition.RecognisedObject;
-
-
-@Disabled
-public class TensorflowVideoRecParserTest {
-
- @Test
- public void recognise() throws Exception {
- TensorflowRESTVideoRecogniser recogniser = new TensorflowRESTVideoRecogniser();
- recogniser.initialize(new HashMap<>());
- try (InputStream stream = getClass().getClassLoader()
- .getResourceAsStream("test-documents/testVideoMp4.mp4")) {
- List objects = recogniser
- .recognise(stream, new DefaultHandler(), new Metadata(), new ParseContext());
-
- assertTrue(objects.size() > 0);
- Set objectLabels = new HashSet<>();
- for (RecognisedObject object : objects) {
- objectLabels.add(object.getLabel());
- }
- assertTrue(objectLabels.size() > 0);
- }
- }
-}
diff --git a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-package/pom.xml b/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-package/pom.xml
deleted file mode 100644
index 17998c7f4ca..00000000000
--- a/tika-parsers/tika-parsers-ml/tika-parser-advancedmedia-package/pom.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-
- tika-parsers-ml
- org.apache.tika
- 4.0.0-SNAPSHOT
-
- 4.0.0
-
- tika-parser-advancedmedia-package
- Apache Tika advanced media package
-
-
-
-
-
-
-
- ${project.groupId}
- tika-parser-advancedmedia-module
- ${project.version}
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- org.apache.tika.parser.advancedmedia
-
-
-
-
-
-
- test-jar
-
-
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- ${maven.shade.version}
-
-
- package
-
- shade
-
-
-
- false
-
-
-
- *:*
-
- module-info.class
- META-INF/*.SF
- META-INF/*.DSA
- META-INF/*.RSA
- META-INF/DEPENDENCIES
- META-INF/MANIFEST.MF
- META-INF/LICENSE.md
- META-INF/NOTICE.md
-
-
-
-
-
- false
-
-
-
-
-
- META-INF/LICENSE
- target/classes/META-INF/LICENSE
-
-
-
-
-
-
-
-
-
-
-
- 3.0.0-rc1
-
-
\ No newline at end of file