- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我试图在手机屏幕上找到特定的词,然后在它们周围显示一个边界框(如果存在)。我遵循以下步骤:
TextRecognizer
对象Rect
在屏幕上绘制。它几乎有效,这是检测结果的屏幕截图,并在记事本应用程序上突出显示了单词hello
:
如您所见,hello
上的半透明黄色框不是很明显。
这里是相关的代码示例。将屏幕截图位图传递给 ML Kit:
InputImage image = InputImage.fromBitmap(screenshotBitmap, 0);
//I checked: image, screen, and overlay view dimensions are exactly the same.
TextRecognizer recognizer = TextRecognition.getClient();
recognizer.process(image)
.addOnSuccessListener(this::processText);
获取识别词的processText
方法:
for (Text.Element element : getElements()) {
String elementText = element.getText();
Rect bounds = element.getBoundingBox(); //Getting the bounding box
if (elementText.equalsIgnoreCase("hello")) { //hello is hardcoded for now
addHighlightCard(bounds.left, bounds.top, bounds.width(), bounds.height());
}
}
最后,addHighlightCard
,它创建并定位您在屏幕截图上看到的 View 。它使用带有 RelativeLayout
的全屏覆盖,因为这种布局允许我指定 subview 的确切位置和宽度。
public void addHighlightCard(int x, int y, int width, int height) {
View highlightCard = inflater.inflate(R.layout.highlight_card, overlayRoot, false);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
params.leftMargin = x;
params.topMargin = y;
highlightCard.setLayoutParams(params);
overlayRoot.addView(highlightCard, params);
}
如您所见,没有进行任何缩放,我捕获了整个屏幕,并且我使用了一个填满整个屏幕(甚至是工具栏)的布局。然后,我认为 ML Kit 返回的坐标应该可以直接用于绘制到屏幕上。但显然我错了,图像似乎在某处缩小了,但我不知道在哪里。
解决方案:事实证明,Media Projection API 虚拟显示器的尺寸不正确导致边界框未对齐。为了避免让这个问题变得更长,我将在此处发布指向 GitHub 存储库的链接,您可以在其中找到一个示例应用程序,该应用程序展示了使用 Media Projection API 并在截图。
最佳答案
我发现您的代码有 4 个潜在问题。
当您在此处创建亮点卡片时:
public void addHighlightCard(int x, int y, int width, int height) {
...
params.leftMargin = x;
params.topMargin = y;
...
}
您指定绝对坐标(屏幕坐标)x
和 y
而不是相对于您的 RelativeLayout
的坐标,这是错误的,因为 RelativeLayout
也有一些关于设备屏幕的偏移量。
要分配正确的坐标,请先为您的RelativeLayout
计算屏幕坐标,然后根据这些坐标调整x
和y
。例如:
public void addHighlightCard(int x, int y, int width, int height) {
...
int[] screenCoordinates = new int[2];
overlayRoot.getLocationOnScreen(screenCoordinates);
int xOffset = screenCoordinates[0];
int yOffset = screenCoordinates[1];
params.leftMargin = x - xOffset;
params.topMargin = y - yOffset;
...
}
但是,如果您的根 View
占据了整个屏幕,那应该不是问题。
我认为这可能是个问题,因为如果您想在另一个 FrameLayout
之上添加一个新的 View
,则应该改用它。但是,我不能确定这是否是一个问题,因为我没有看到完整的代码。
您没有向我们展示您是如何使用 MediaProjection 做到这一点的,因此这也可能是一个问题。我使用了一种不同的方式来捕获您可以在下面看到的屏幕。
您正在从 LayoutInflater
膨胀一个 View
以突出显示找到的文本。为了测试,我通过组合 ShapeDrawable
和 View
做了一些不同的事情,比如:
...
ShapeDrawable drawable = new ShapeDrawable();
drawable.getPaint().setColor(Color.YELLOW);
drawable.getPaint().setStyle(Paint.Style.STROKE);
drawable.getPaint().setStrokeWidth(5f);
View shapeView = new View(decorView.getContext());
shapeView.setBackground(drawable);
...
下面将提供完整的代码。
既然您提到您的 RelativeLayout
占据了整个屏幕,我决定创建一个示例项目来证明与您的项目类似的项目运行良好。
下面是解释和相关代码。
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.example.myapplication"
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'com.google.android.gms:play-services-mlkit-text-recognition:16.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
在这里,为了截图,我使用了以下代码:
Bitmap bitmap = Bitmap.createBitmap(decorView.getWidth(),
decorView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
decorView.draw(canvas);
InputImage image = InputImage.fromBitmap(bitmap, 0);
我在 OnGlobalLayoutListener
中执行此操作以确保装饰 View 具有适当的宽度和高度。好的,该类的完整代码如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setAdapter(new RecyclerViewAdapter(this));
recyclerView.setLayoutManager(new LinearLayoutManager(this));
View decorView = getWindow().getDecorView();
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
decorView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// take a screenshot of your screen
Bitmap bitmap = Bitmap.createBitmap(decorView.getWidth(),
decorView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
decorView.draw(canvas);
InputImage image = InputImage.fromBitmap(bitmap, 0);
TextRecognizer recognizer = TextRecognition.getClient();
recognizer.process(image).addOnSuccessListener(new OnSuccessListener<Text>() {
@Override
public void onSuccess(Text text) {
for (Text.TextBlock textBlock : text.getTextBlocks()) {
if ("hello".equalsIgnoreCase(textBlock.getText())) {
Rect box = textBlock.getBoundingBox();
int left = box.left;
int top = box.top;
int right = box.right;
int bottom = box.bottom;
ShapeDrawable drawable = new ShapeDrawable();
drawable.getPaint().setColor(Color.YELLOW);
drawable.getPaint().setStyle(Paint.Style.STROKE);
drawable.getPaint().setStrokeWidth(5f);
View shapeView = new View(decorView.getContext());
shapeView.setBackground(drawable);
FrameLayout rootView = findViewById(R.id.root_view);
int[] location = new int[2];
rootView.getLocationOnScreen(location);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(right - left,
bottom - top);
params.setMargins(left - location[0],
top - location[1],
right - location[0],
bottom - location[1]);
rootView.addView(shapeView, params);
}
}
}
});
}
});
}
private static class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {
private final Context context;
private final String[] elements = new String[] {"Hello", "Hello", "Bye", "Hello", "Hi there", "Hello"};
private RecyclerViewAdapter(Context context) {
this.context = context;
}
@NonNull
@Override
public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View item = LayoutInflater.from(context).
inflate(R.layout.list_item, parent, false);
return new RecyclerViewHolder(item);
}
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
holder.textView.setText(elements[position]);
}
@Override
public int getItemCount() {
return elements.length;
}
public static class RecyclerViewHolder extends RecyclerView.ViewHolder {
private final TextView textView;
public RecyclerViewHolder(@NonNull View itemView) {
super(itemView);
this.textView = itemView.findViewById(R.id.element_view);
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="30dp"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</LinearLayout>
</FrameLayout>
如您所见,我使用 FrameLayout
作为 Root View 。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical">
<TextView
android:id="@+id/element_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:fontFamily="google-sans-medium"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000"/>
</LinearLayout>
布局没有什么特别之处 - 只是 RecyclerView
的简单布局。
所有 4 个“Hello”结果都以黄色突出显示。
如果您不是从 Activity
获取显示大小,请确保您使用正确的方法获取显示大小(在您的 GitHub 项目中,您是从 Service
获取它>) 因为你需要真实的显示尺寸,而不是其他东西。因此,请按以下方式操作:
// get width and height
WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getRealSize(size);
mWidth = size.x;
mHeight = size.y;
因此,在您的示例中,您必须将方法更改为:
private void createVirtualDisplay() {
// get width and height
WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getRealSize(size);
mWidth = size.x;
mHeight = size.y;
...
}
就是这样。
关于Android:在屏幕上定位文字。 Google ML Kit 边界框有点偏离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66624836/
这里的这个问题对 updating Google Sheets charts linked to Google slides 有一个简洁的解决方案. function onOpen() { var
我正在尝试将 Google 表单添加到 Google 类作业中,但似乎不可能。 首先,它在这里 ( https://developers.google.com/classroom/reference/
出于某种原因,无论我做什么以及我如何尝试,这个日期格式化程序都不起作用。工具提示仍然显示错误的格式。你可以试试代码here . 在代码中我必须注释掉 formatter.format(dataTabl
我目前正在使用访问 token 和刷新 token 从 Google Analytics Reporting API (v4) 中提取数据。当我致力于自动从 Google Analytics 中提取数
我已在 Google 云端硬盘中创建了一个文件夹,例如测试一下,放入3个文件 a.jpg, b.jpg, c.jpg 我希望在同一帐户下的 Google 电子表格中访问文件,例如生成图像文件的链接,可
电子表格 A 是欢迎新移民来到我们小镇的团队的主数据源。它里面有大量非常敏感的数据,不能公开,哪怕是一点点。 (我们谈论的是 child 的姓名和出生日期以及他们在哪里上学……保证电子表格 A 的安全
有没有办法在 Google 文档中编写 Google Apps 脚本以从 Google 表格中检索仅限于非空白行的范围并将这些行显示为表格? 我正在寻找一个脚本,用于使用 Google Apps 脚本
有没有办法在 Google 文档中编写 Google Apps 脚本以从 Google 表格中检索仅限于非空白行的范围并将这些行显示为表格? 我正在寻找一个脚本,用于使用 Google Apps 脚本
尝试检索存储在 google firebase 中名为条目的节点下的表单条目,并使用谷歌工作表中的脚本编辑器附加到谷歌工作表。 我已将 FirebaseApp 库添加到谷歌表脚本编辑器。然后我的代码看
是否可以将我的 Web 应用程序的登录限制为仅限 google 组中的帐户? 我不希望每个人都可以使用他们的私有(private) gmail 登录,而只能使用我的 google 组中的用户。 最佳答
我们想使用 Google 自定义搜索实现 Google 附加链接搜索框。在谷歌 documentation , 我发现我们需要包含以下代码来启用附加链接搜索框 { "@context"
我想将特定搜索词的 Google 趋势图表添加到我的 Google Data Studio 报告中,但趋势不是数据源列表中的选项。我也找不到嵌入 JavaScript 的选项。是否可以将趋势图表添加到
是否可以将文件从 Google Drive 复制到 Google Cloud Storage?我想它会非常快,因为两者都在类似的存储系统上。 我还没有看到有关无缝执行此操作的任何方法的任何信息,而无需
之间有什么区别 ga('send', 'pageview', { 'dimension1': 'data goes here' }); 和 ga('set', 'dimension1', 'da
我正在尝试记录每个博客站点作者的点击率。 ga('send', 'pageview'); (in the header with the ga code to track each page) ga(
我设置了 Google Tag Manager 和 2 个数据层变量:一个用于跟踪用户 ID,传递给 Google Analytics 以同步用户 session ,另一个用于跟踪访问者类型。 在使用
我在我们的网站上遇到多个职位发布的问题。 我们在加拿大多个地点提供工作机会。所有职位页面都包含一个“LD+JSON ”职位发布的结构化数据,基于 Google 的职位发布文档: https://dev
公司未使用 Google 套件,使用个人(消费者)帐户(甚至是 Google 帐户)违反公司政策。 需要访问 Google Analytics - 没有 Google 帐户是否可能? 谢谢 最佳答案
我想分析人们使用哪些搜索关键字在 Play 商店中找到我的应用。 那可能吗?我怎么能这样做? 最佳答案 自 2013 年 10 月起,您可以关联您的 Google Analytics(分析)和 Goo
Google Now 和 Google Keep 中基于时间和位置的提醒与 Google Calendar 事件提醒不同。是否有公共(public) API 可以访问 Now 和 Keep 中的这些事
我是一名优秀的程序员,十分优秀!