当前位置: 首页 > news >正文

如何使用Android NDK将头像变成“遗像”

看完本文的标题,可能有人要打我。你说黑白的老照片不好吗?非要说什么遗像,我现在就把你变成遗像!好了,言归正传。我想大部分人都用过美颜相机或者剪映等软件吧,它们的滤镜功能是如何实现的,有人想过吗?难道要使用Java/Kotlin遍历Bitmap,然后处理到手机发烫吗?学过计算机图形学的应该知道,图片或视频的图像数据的最小单位是像素px。视频编码H.264的YUV数据不也是视频像素数据嘛,只不过有很多帧。

代码实现

本篇文章,我来教大家简单的处理图像中一个个的像素点的色值,从而达到改变图片风格的效果。首先我们创建一个Native C++项目。
截屏2024-04-11 22.24.19.png
然后我们需要写个简单的xml布局。

<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><ImageViewandroid:id="@+id/iv_display"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/btnPicProcess"android:layout_width="match_parent"android:layout_height="wrap_content"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintBottom_toBottomOf="parent"android:text="黑白"/></androidx.constraintlayout.widget.ConstraintLayout>

接下来我们需要实现Activity的代码了。注意这里有个external方法,它相当于java中的带native关键字的方法,只不过这是kotlin的写法。在handleBitmap()方法中调用底层方法处理图像。就这么一个简单的流程,因为我们把图像处理的逻辑全部都放到c++底层代码中了。之所以把图像处理的逻辑全权交给C++的原因我想大家应该都知道了,IO是非常耗时的,而两层循环的时间复杂度是O(n²)。那就是java使用jni跟c++频繁通信会大量调输入输出流,时间都损耗在bitmap的setPixels方法上了。那还不如你处理好了给我结果就好了,我不需要关心过程。

package com.dorachat.myapplicationimport android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.dorachat.myapplication.databinding.ActivityMainBindingclass MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)val bitmap = BitmapFactory.decodeResource(resources, R.drawable.pic)binding.ivDisplay.setImageBitmap(bitmap)binding.btnPicProcess.setOnClickListener {binding.ivDisplay.setImageBitmap(handleBitmap(bitmap))}}/*** jni处理图片。*/private fun handleBitmap(bitmap: Bitmap) : Bitmap {val bmp = bitmap.copy(Bitmap.Config.ARGB_8888, true);nativeProcessBitmap(bmp);return bmp;}/*** 黑白特效,调用native底层方法。*/external fun nativeProcessBitmap(bitmap: Bitmap)companion object {// Used to load the 'myapplication' library on application startup.init {System.loadLibrary("myapplication")}}
}

我们再来看下CMakeLists.txt的代码,注意target_link_libraries中要链接jnigraphics。

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
project("myapplication")# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
# is preferred for the same purpose.
#
# In order to load a library into your app from Java/Kotlin, you must call
# System.loadLibrary() and pass the name of the library defined here;
# for GameActivity/NativeActivity derived applications, the same library name must be
# used in the AndroidManifest.xml file.
add_library(${CMAKE_PROJECT_NAME} SHARED# List C/C++ source files with relative paths to this CMakeLists.txt.native-lib.cpp)# Specifies libraries CMake should link to your target library. You
# can link libraries from various origins, such as libraries defined in this
# build script, prebuilt third-party libraries, or Android system libraries.
target_link_libraries(${CMAKE_PROJECT_NAME}# List libraries link to the target libraryandroidlogjnigraphics)

最后就是我们最最重要的底层代码了。通过调用AndroidBitmap_getInfo来读取图片像素数据,并将其保存在AndroidBitmapInfo中。AndroidBitmap_lockPixels 是 Android Native Development Kit (NDK) 中的一个函数,用于锁定一个 android.graphics.Bitmap 对象的像素数据,以便在 Native 代码中进行像素级别的操作。这个函数允许在 C 或 C++ 代码中直接访问 Android 应用中的位图像素数据,以便进行图像处理、渲染或其他图像相关的操作。最后记得调用AndroidBitmap_unlockPixels解锁。至于为什么是(R+G+B)/3,这个问题要问我为什么这么算,我能力有限教不了你们,你得问计算机图像处理学的专家,哈哈。当将彩色图像转换为黑白图像时,通常采用灰度化的方法,即将红、绿、蓝三个通道的数值取平均值,以获得灰度图像。因此,将RGB图像转换为黑白图像时,每个像素的灰度值通常是红、绿、蓝通道值的平均值,即(R+G+B)/3。

#include <jni.h>
#include <string>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <android/bitmap.h>#define MAKE_RGB565(r, g, b) ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3))
#define MAKE_ARGB(a, r, g, b) ((a&0xff)<<24) | ((r&0xff)<<16) | ((g&0xff)<<8) | (b&0xff)#define RGB565_R(p) ((((p) & 0xF800) >> 11) << 3)
#define RGB565_G(p) ((((p) & 0x7E0 ) >> 5)  << 2)
#define RGB565_B(p) ( ((p) & 0x1F  )        << 3)#define RGB8888_A(p) (p & (0xff<<24) >> 24 )
#define RGB8888_R(p) (p & (0xff<<16) >> 16 )
#define RGB8888_G(p) (p & (0xff<<8)  >> 8 )
#define RGB8888_B(p) (p & (0xff) )#define RGBA_A(p) (((p) & 0xFF000000) >> 24)
#define RGBA_R(p) (((p) & 0x00FF0000) >> 16)
#define RGBA_G(p) (((p) & 0x0000FF00) >>  8)
#define RGBA_B(p)  ((p) & 0x000000FF)
#define MAKE_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))extern "C" JNIEXPORT void JNICALL
Java_com_dorachat_myapplication_MainActivity_nativeProcessBitmap(JNIEnv *env,jobject instance,jobject bitmap) {if (bitmap == NULL) {return;}AndroidBitmapInfo bitmapInfo;memset(&bitmapInfo, 0, sizeof(bitmapInfo));// Need add "jnigraphics" into target_link_libraries in CMakeLists.txtAndroidBitmap_getInfo(env, bitmap, &bitmapInfo);// Lock the bitmap to get the buffervoid *pixels = NULL;int res = AndroidBitmap_lockPixels(env, bitmap, &pixels);// From top to bottomint x = 0, y = 0;for (y = 0; y < bitmapInfo.height; ++y) {// From left to rightfor (x = 0; x < bitmapInfo.width; ++x) {int a = 0, r = 0, g = 0, b = 0;void *pixel = NULL;// Get each pixel by formatif (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {pixel = ((uint32_t *) pixels) + y * bitmapInfo.width + x;int r, g, b;uint32_t v = *((uint32_t *) pixel);r = RGB8888_R(v);g = RGB8888_G(v);b = RGB8888_B(v);int sum = r + g + b;*((uint32_t *) pixel) = MAKE_ARGB(0xff, sum / 3, sum / 3, sum / 3);} else if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565) {pixel = ((uint16_t *) pixels) + y * bitmapInfo.width + x;int r, g, b;uint16_t v = *((uint16_t *) pixel);r = RGB565_R(v);g = RGB565_G(v);b = RGB565_B(v);int sum = r + g + b;*((uint16_t *) pixel) = MAKE_RGB565(sum / 3, sum / 3, sum / 3);}}}AndroidBitmap_unlockPixels(env, bitmap);
}
效果演示

知道你们喜欢清纯的,来了。最后不忘留个赞吧。
20240411_230705.gif

相关文章:

如何使用Android NDK将头像变成“遗像”

看完本文的标题&#xff0c;可能有人要打我。你说黑白的老照片不好吗&#xff1f;非要说什么遗像&#xff0c;我现在就把你变成遗像&#xff01;好了&#xff0c;言归正传。我想大部分人都用过美颜相机或者剪映等软件吧&#xff0c;它们的滤镜功能是如何实现的&#xff0c;有人…...

python判断字符串是否为回文串的详细解析与实现

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;回文串的定义与背景 二、判断回文串的基本思路 示例解析 三、代码实…...

148.【Windows DOS命令脚本文件】

Window待处理脚本 (一)、批处理编程初步体验1.什么是批处理程序&#xff1f;(1).批处理程序的定义(2).如何编辑批处理程序 2.批处理程序可以做什么&#xff1f;(1).匹配规则删除文件(2).新建文件&#xff0c;日志等(3).创建计算机病毒等 3.一个基本的批处理文件(1).带盘符的输出…...

推荐网站(13)plantumlb自动帮我们创建 UML 图表

今天推荐一个网站plantumlb&#xff0c;它可以自动帮我们创建 UML 图表。 plantumlb网站中的PlantUML是一个开源工具&#xff0c;它允许用户使用简单的文本描述来创建UML&#xff08;统一建模语言&#xff09;图表。PlantUML支持多种类型的UML图表&#xff0c;包括用例图、类图…...

【vue-1】vue入门—创建一个vue应用

最近在闲暇时间想学习一下前端框架vue&#xff0c;主要参考以下两个学习资料。 官网 快速上手 | Vue.js b站学习视频 2.创建一个Vue3应用_哔哩哔哩_bilibili 一、创建一个vue3应用 <!DOCTYPE html> <html lang"en"> <head><meta charset&q…...

【微信小程序开发】小程序前后端交互--发送网络请求实战解析

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…...

【NOIP2013普及组复赛】题2:表达式求值

题2&#xff1a;表达式求值 【题目描述】 给定一个只包含加法和乘法的算术表达式&#xff0c;请你编程计算表达式的值。 【输入文件】 输入仅有一行&#xff0c;为需要你计算的表达式&#xff0c;表达式中只包含数字、加法运算符 “ ” “” “”和乘法运算符 “ ∗ ” “…...

datasheet芯片数据手册—新手入门学习(二)【8-18】

参考芯片手册已经上传&#xff0c;可自行下载 因为芯片参考手册内容比较多&#xff0c;故再一次介绍本文内容主要讲解章节。 目录 8、内容介绍 命令真值表 9、Command Definitions 10、READ Operations &#xff08;1&#xff09;页面读取操作 &#xff08;2&#xff…...

UE5 双手握剑的实现(逆向运动学IK)

UE5 双手握剑的实现 IK 前言 什么是IK&#xff1f; UE官方给我们提供了很多对于IK处理的节点&#xff0c;比如ABRIK、Two Bone IK、Full Body IK 、CCD IK等&#xff0c;但是看到这&#xff0c;很多人就好奇了&#xff0c;什么是IK&#xff1f; 首先我们来看看虚幻小白人的骨…...

Java中的接口(Interface)及其与抽象类的区别

一、技术难点 在Java中&#xff0c;接口是一种完全抽象的类&#xff0c;主要用于定义一组方法的规范&#xff0c;但不提供具体的实现。接口的技术难点主要体现在以下几个方面&#xff1a; 方法的定义与实现分离&#xff1a;接口只定义了方法的签名&#xff0c;没有方法体&…...

代码随想录算法训练营第十四天(py)| 二叉树 | 递归遍历、迭代遍历、统一迭代

1 理论基础 1.1 二叉树的种类 满二叉树 只有度为0和2的节点&#xff0c;且度为0的节点在同一层。 深度为k&#xff0c;有2^k-1个节点 完全二叉树 除了最底层可能没填满&#xff0c;其余每层节点数都达到最大。并且最底层节点全部集中在左边。 二叉搜索树 是一个有数值…...

Golang并发编程-协程goroutine初体验

文章目录 前言一、Goroutine适合的使用场景二、Goroutine的使用1. 协程初体验 三、WaitGroupWaitGroup 案例一WaitGroup 案例二 总结 前言 学习Golang一段时间了&#xff0c;一直没有使用过goroutine来提高程序执行效率&#xff0c;在一些特殊场景下&#xff0c;还是有必须开启…...

驱动与系统学习网址

DRM&#xff08;Direct Rendering Manager&#xff09;学习简介-CSDN博客 Android Qcom Display学习(零)-CSDN博客 https://blog.csdn.net/hexiaolong2009/category_9705063.htmlhttps://blog.csdn.net/hexiaolong2009/category_9705063.htmlRender Hell —— 史上最通俗易懂…...

OAuth2.0

OAuth2.0 OAuth2.0是一种授权框架&#xff0c;用于授权第三方应用访问用户资源的方式。它允许用户将自己的信息&#xff08;如照片、视频等&#xff09;存储在一个服务提供商中&#xff0c;然后授权第三方应用访问这些信息&#xff0c;而无需提供用户名和密码给第三方应用。OAu…...

测试testing10

测试testing10...

在Java中实现泛型(Generics)的深入解析

在Java中&#xff0c;泛型&#xff08;Generics&#xff09;是一个强大的工具&#xff0c;它允许我们在编译时定义类型参数&#xff0c;使代码更加灵活、可重用和类型安全。下面&#xff0c;我将从技术难点、面试官关注点、回答吸引力以及代码举例四个方面&#xff0c;详细解析…...

每周题解:繁忙的都市

题目链接 繁忙的都市 题目描述 城市 C 是一个非常繁忙的大都市&#xff0c;城市中的道路十分的拥挤&#xff0c;于是市长决定对其中的道路进行改造。城市 C 的道路是这样分布的&#xff1a;城市中有 n n n 个交叉路口&#xff0c;有些交叉路口之间有道路相连&#xff0c;两…...

linux之防火墙工具

netfilter Linux防火墙是由Netfilter组件提供的&#xff0c;Netfilter工作在内核空间&#xff0c;集成在linux内核中。 Netfilter在内核中选取五个位置放了五个hook(勾子) function(INPUT、OUTPUT、FORWARD、PREROUTING、POSTROUTING)&#xff0c;而这五个hook function向用户…...

【Python】—— 高阶函数

目录 &#xff08;一&#xff09;体验高阶函数 &#xff08;二&#xff09;内置高阶函数 2.1 map(&#xff09; 2.2 reduce() 2.3 filter() Python中的高阶函数是指那些接受函数作为参数&#xff0c;或者返回函数作为结果的函数。这种特性让Python的函数编程能力非常强大&…...

逻辑分析仪 - 采样率/采样深度

采样深度&#xff08;Sampling Depth&#xff09; 采样深度指的是逻辑分析仪在一次捕获过程中可以记录的最大样本数量。简单来说&#xff0c;采样深度越大&#xff0c;逻辑分析仪可以记录的数据量就越多。这对于分析长时间的信号变化或复杂的信号序列非常重要。 采样率&#…...

Python开发者首次使用Taotoken接入大模型API的完整步骤指南

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 Python开发者首次使用Taotoken接入大模型API的完整步骤指南 对于Python开发者而言&#xff0c;接入大模型API进行应用开发已成为一…...

新手村任务:成为一个架构师需要哪些装备?

新手村任务:成为一个架构师需要哪些装备? 一、前言 如果你刚入行不久,想成为一名架构师,那这篇文章就是为你写的。 我们把成为架构师比作一个RPG游戏,你是主角,需要收集各种装备、刷经验、升级技能。 新手村的第一个任务就是:了解你需要哪些装备。 二、架构师技能树…...

GEO生成引擎优化:当AI成为信息分发的主角,品牌如何抢占对话窗口?

当用户不再"搜索-浏览"&#xff0c;而是直接"AI提问-获取答案"&#xff0c;传统SEO的逻辑正在被彻底改写。2026年&#xff0c;GEO&#xff08;Generative Engine Optimization&#xff0c;生成式引擎优化&#xff09;已经从概念走向规模化落地。本文从技术…...

2027考研全套资料免费分享

备战27考研最全备考资料整理完毕&#xff0c;一路走来深知备考搜集资料耗费大量时间&#xff0c;浪费不少精力。特意整理2027考研全科完整版资源&#xff0c;全部打包汇总&#xff0c;零基础考生直接拿来就能使用&#xff0c;省去四处搜集资料的烦恼。资料内含&#xff1a;&…...

在Hermes Agent项目中接入Taotoken作为自定义模型供应商

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 在Hermes Agent项目中接入Taotoken作为自定义模型供应商 基础教程类&#xff0c;针对使用Hermes Agent框架的开发者&#xff0c;详…...

如何用WaveTools终极优化《鸣潮》游戏性能:从卡顿到丝滑的完整指南

如何用WaveTools终极优化《鸣潮》游戏性能&#xff1a;从卡顿到丝滑的完整指南 【免费下载链接】WaveTools &#x1f9f0;鸣潮工具箱 项目地址: https://gitcode.com/gh_mirrors/wa/WaveTools 如果你正在玩《鸣潮》却频繁遭遇帧率波动、画面卡顿或操作延迟&#xff0c;那…...

java项目011-ssm 宠物医院系统

java项目011-ssm 宠物医院系统 是一款基于springspringmvcmybatis的宠物系统&#xff0c; 包含界面布局、医生信息管理、客户信息管理、宠物管理、浏览管理、 诊断管理、医生管理、用户管理 其中医生管理、用户管理只能管理员有权限进行操作。 采用spingboot方式启动 运行截图...

3大技术突破:重新定义Switch游戏安装性能极限

3大技术突破&#xff1a;重新定义Switch游戏安装性能极限 【免费下载链接】Awoo-Installer A No-Bullshit NSP, NSZ, XCI, and XCZ Installer for Nintendo Switch 项目地址: https://gitcode.com/gh_mirrors/aw/Awoo-Installer Awoo Installer是一款专为破解版Nintendo…...

基于Arduino Uno与MQ-2传感器的智能气体检测报警系统DIY全攻略

1. 项目概述与核心思路最近在捣鼓家里的智能安防&#xff0c;琢磨着能不能自己做一个成本可控、反应灵敏的气体检测报警装置。市面上成品烟雾报警器虽然成熟&#xff0c;但要么功能单一&#xff0c;要么价格不菲&#xff0c;而且很难根据自己的需求进行定制化调整&#xff0c;比…...

3大突破性功能:用HiveWE革新你的魔兽争霸III地图创作体验

3大突破性功能&#xff1a;用HiveWE革新你的魔兽争霸III地图创作体验 【免费下载链接】HiveWE A Warcraft III world editor. 项目地址: https://gitcode.com/gh_mirrors/hi/HiveWE 还在为传统魔兽争霸III编辑器缓慢的加载速度和复杂的操作界面而烦恼吗&#xff1f;Hive…...