我正在做一个应用程序,涉及一个CameraX应用程序接口和位图,我的目标是使相机不仅捕获一个简单的图像,但采取图像和绘制水印文本在图像顶部的文件,但方位,俯仰和滚动显示为空的结果当位图完成,这是它的结果。[![ JPEG文件的实际输出。如果我没有解释正确或者在解释时犯了错误,请尽快让我知道,还有一些问题,我会回答他们。提前谢谢你。
我将赋予图像更多的含义,并将其从保加利亚语翻译成英语,以及它代表什么:倾斜的方位:where(西,南,北,东)方位,倾斜的->:Посока,Наклон
private val mSensorEvent: SensorEvent? = null
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
//Azimuth, Pitch and Roll
val azimuthWMText = resources.getString(R.string.value_format_2, mSensorEvent?.values?.get(0))
val pitchWMText = resources.getString(R.string.value_format_2, mSensorEvent?.values?.get(1))
val rollWMText = resources.getString(R.string.value_format_2, mSensorEvent?.values?.get(2))
//Bitmap that contains the addWatermark method and detecting the new photo path that is been taken and implements the watermark
//BitmapFactory.decodeFile(path.toString(), BitmapFactory.Options()) -> through File
//resources, R.layout.activity_preview_photo, BitmapFactory.Options() -> through resources
val originalBitmap = AddWatermark().addWatermark(
BitmapFactory.decodeFile(photoFile.toString(), BitmapFactory.Options()),
firstWatermarkText = "Дължина: $longitudeWM${resources.getString(R.string.degrees)}, Ширина: $latitudeWM${resources.getString(R.string.degrees)}",
secondWatermarkText = "Височина: ${altitudeWM}м, Ориентация: $orientationWM",
thirdWatermarkText = "Точност: Хоризонтална: ${hozAccuracyWM}м, Вертикална: ${verAccuracyWM}м",
fourthWatermarkText = "Посока: where $azimuthWMText${resources.getString(R.string.degrees)}",
fifthWatermarkText = "Наклон: pitchTilt $pitchWMText${resources.getString(R.string.degrees)}, rollTilt $rollWMText${resources.getString(R.string.degrees)}",
sixthWatermarkText = "Дата и Час: $dateTimeFormatWMText",
AddWatermark.WatermarkOptions(
AddWatermark.Corner.TOP_LEFT,
textSizeToWidthRation = 0.017f,
paddingToWidthRatio = 0.03f,
Color.parseColor("#FF0000"),
shadowColor = Color.BLACK,
strokeOutline = null,
typeface = null
)
)
previewView.bitmap.let { originalBitmap }
val outputStream = FileOutputStream(photoFile)
originalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream)
outputStream.flush()
outputStream.close()
Toast.makeText(
this@CameraActivity,
"Обработването и запазено успешно! Запазено е в: $photoFile",
Toast.LENGTH_LONG
).show()
}
水印类代码:
class AddWatermark : AppCompatActivity() {
//Adding watermark method is here for declaring on to the top
fun addWatermark(
bitmap: Bitmap,
firstWatermarkText: String,
secondWatermarkText: String,
thirdWatermarkText: String,
fourthWatermarkText: String,
fifthWatermarkText: String,
sixthWatermarkText: String,
options: WatermarkOptions = WatermarkOptions()): Bitmap {
val result = bitmap.copy(bitmap.config, true)
val canvas = Canvas(result)
val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG)
//val strokePaint = Paint()
//We are including the Enum class and connecting with the data class WatermarkOptions variable
paint.textAlign = when (options.corner) {
//We include the alignment LEFT from Enum class and connecting with Paint variable
Corner.TOP_LEFT,
Corner.BOTTOM_LEFT -> Paint.Align.LEFT
//We include the alignment RIGHT from Enum class and connecting with Paint variable
Corner.TOP_RIGHT,
Corner.BOTTOM_RIGHT -> Paint.Align.RIGHT
}
/*strokePaint.textAlign = when (options.corner) {
Corner.TOP_LEFT,
Corner.BOTTOM_LEFT -> Paint.Align.LEFT
Corner.TOP_RIGHT,
Corner.BOTTOM_RIGHT -> Paint.Align.RIGHT
}
*/
//We connect the new textSize variable with the bitmap width(default is 0) and we multiply with the WatermarkOption's textSize
val textSize = result.width * options.textSizeToWidthRation
//Connecting the Paint textSize variable with the new textSize variable
paint.textSize = textSize//70.5f
//Connecting the Paint color variable with the WatermarkOptions textColor
paint.color = options.textColor
//If the shadowColor of the WMOptions is not null, then we make it as a Paint shadowLayer variable
if (options.shadowColor != null) {
paint.setShadowLayer( 2.5f, 0f, 0f, options.shadowColor)
}
/*if (options.strokeOutline != null) {
strokePaint.textSize = textSize//72f
strokePaint.color = options.strokeOutline
strokePaint.style = Paint.Style.STROKE
strokePaint.strokeWidth = 4.17f
}
*/
//If typeface of the WMOptions is not null,we make paint typeface variable and connecting with the WMOptions variable
if (options.typeface != null) {
paint.typeface = options.typeface
}
//We connect the new padding variable with the bitmap width(default is 0) and multiply with WMOptions padding
val padding = result.width * options.paddingToWidthRatio
//Create a variable that has something to do with the coordinates method
val coordinates = calculateCoordinates(
firstWatermarkText,
secondWatermarkText,
thirdWatermarkText,
fourthWatermarkText,
fifthWatermarkText,
sixthWatermarkText,
paint,
options,
canvas.width,
canvas.height,
padding)
/**drawText text as a Watermark, using Canvas**/
//canvas.drawText(firstWatermarkText, coordinates.x, coordinates.y, strokePaint)
//canvas.drawText(secondWatermarkText, coordinates.x, 240f, strokePaint) //We change the Y horizontal coordinates by typing the float number
//canvas.drawText(thirdWatermarkText, coordinates.x, 310f, strokePaint)
//canvas.drawText(fourthWatermarkText, coordinates.x, 380f, strokePaint)
//canvas.drawText(fifthWatermarkText, coordinates.x, 450f, strokePaint)
when (Build.VERSION.SDK_INT) {
//Android 11
30 -> {
canvas.drawText(firstWatermarkText, coordinates.x, coordinates.y, paint)
canvas.drawText(secondWatermarkText, coordinates.x, 240f, paint)
canvas.drawText(thirdWatermarkText, coordinates.x, 310f, paint)
canvas.drawText(fourthWatermarkText, coordinates.x, 380f, paint)
canvas.drawText(fifthWatermarkText, coordinates.x, 450f, paint)
canvas.drawText(sixthWatermarkText, coordinates.x, 520f, paint)
}
//Android 9
28 -> {
canvas.drawText(firstWatermarkText, coordinates.x, coordinates.y, paint)
canvas.drawText(secondWatermarkText, coordinates.x, 240f, paint)
canvas.drawText(thirdWatermarkText, coordinates.x, 310f, paint)
canvas.drawText(fourthWatermarkText, coordinates.x, 380f, paint)
canvas.drawText(fifthWatermarkText, coordinates.x, 450f, paint)
canvas.drawText(sixthWatermarkText, coordinates.x, 520f, paint)
}
//Android 5.0
21 -> {
canvas.drawText(firstWatermarkText, coordinates.x, coordinates.y, paint)
canvas.drawText(secondWatermarkText, coordinates.x, 270f, paint)
canvas.drawText(thirdWatermarkText, coordinates.x, 330f, paint)
canvas.drawText(fourthWatermarkText, coordinates.x, 420f, paint)
canvas.drawText(fifthWatermarkText, coordinates.x, 480f, paint)
canvas.drawText(sixthWatermarkText, coordinates.x, 540f, paint)
}
}
return result
}
//This it he corner alignment calculation method and using it on the drawText
private fun calculateCoordinates(
firstWatermarkText: String,
secondWatermarkText: String,
thirdWatermarkText: String,
fourthWatermarkText: String,
fifthWatermarkText: String,
sixthWatermarkText: String,
paint: Paint,
options: WatermarkOptions,
width: Int,
height: Int,
padding: Float
): PointF {
val x = when (options.corner) {
Corner.TOP_LEFT,
Corner.BOTTOM_LEFT -> {
padding
}
Corner.TOP_RIGHT,
Corner.BOTTOM_RIGHT -> {
width - padding
}
}
val y = when (options.corner) {
Corner.BOTTOM_LEFT,
Corner.BOTTOM_RIGHT -> {
height - padding
}
Corner.TOP_LEFT,
Corner.TOP_RIGHT -> {
val bounds = Rect()
paint.getTextBounds(firstWatermarkText, 0, firstWatermarkText.length, bounds)
paint.getTextBounds(secondWatermarkText, 0, secondWatermarkText.length, bounds)
paint.getTextBounds(thirdWatermarkText, 0, thirdWatermarkText.length, bounds)
paint.getTextBounds(fourthWatermarkText, 0, fourthWatermarkText.length, bounds)
paint.getTextBounds(fifthWatermarkText, 0, fifthWatermarkText.length, bounds)
paint.getTextBounds(sixthWatermarkText, 0, sixthWatermarkText.length, bounds)
val textHeight = bounds.height()
textHeight + padding
}
}
return PointF(x, y)
}
enum class Corner {
TOP_LEFT,
TOP_RIGHT,
BOTTOM_LEFT,
BOTTOM_RIGHT
}
data class WatermarkOptions(
val corner: Corner = Corner.BOTTOM_RIGHT,
val textSizeToWidthRation: Float = 0.04f,
val paddingToWidthRatio: Float = 0.03f,
@ColorInt val textColor: Int = Color.parseColor("#FFC800"),
@ColorInt val shadowColor: Int? = Color.BLACK,
@ColorInt val strokeOutline: Int? = Color.BLACK,
val typeface: Typeface? = null
)
}
发布于 2021-10-05 13:59:11
好了,我发现了我的问题,我没有使用新的事件,而是使用了“onSensorChanged( mSensorEvent
:SensorEvent?)”中的传感器事件。相反,并重新转换所有的lateinit变量,所以这是我问题的答案:
private var azimuthDegree: Double = 0.0
private var currentAzimuth: Double = 0.0
private var currentPitch: Double = 0.0
private var currentRoll: Double = 0.0
//'dirs' is the directions array list of type String and 'currentDirection' will be used for recasting
private var dirs = ArrayList<String>()
private lateinit var currentDirection: String
override fun onResume() {
super.onResume()
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
actionBar?.hide()
//Makes a register to the listener about the default sensor for the Azimuth orientation type
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
SensorManager.SENSOR_DELAY_GAME)
}
override fun onPause() {
super.onPause()
//If it fails, to unregister the listener from it's sensor
mSensorManager.unregisterListener(this)
}
/**The sensor method initializes the type sensors and calculates the gravity and geomagnetic field**/
@SuppressLint("CutPasteId")
override fun onSensorChanged(event: SensorEvent?) {
//Portrait measurements - Correct by default
try {
val windowOrientation = windowManager.defaultDisplay.orientation
//Azimuth
degreeAzimuth = event!!.values[0].toDouble()
val azimuthTxt = findViewById<TextView>(R.id.azimuthText)
//This makes sure that the calculations between Pitch and Roll, doesn't switch places when the orientation is turned between Portrait and Landscape by default
var degreeRoll: Double
var degreePitch: Double
if (windowOrientation == 1 || windowOrientation == 3) {
degreeRoll = event.values[1].toDouble()
degreePitch = event.values[2].toDouble()
} else {
degreeRoll = event.values[2].toDouble()
degreePitch = event.values[1].toDouble()
}
//Orientation calculations for the Pitch in degrees, when it's Portrait, Landscape, Reverse Portrait and Reverse Landscape, to stay the same case with each and every value
when (windowOrientation) {
Surface.ROTATION_0 -> {
degreePitch += 90
}
1 -> {
if (abs(degreeRoll) > 90) {
degreePitch -= 90
degreeRoll = if (degreeRoll < 0)
-(180 + degreeRoll)
else
180 - degreeRoll
} else {
degreePitch = 90 - degreePitch
}
}
2 -> {
degreePitch = if (degreePitch < 90)
90 - degreePitch
else
-(degreePitch - 90)
degreeRoll = -degreeRoll
}
3 -> {
if (abs(degreeRoll) > 90) {
degreePitch = -(90 + degreePitch)
degreeRoll = if (degreeRoll < 0)
-(180 + degreeRoll)
else
180 - degreeRoll
} else {
degreePitch += 90
degreeRoll = -degreeRoll
}
}
}
/*Calculations when Azimuth is been turned with a new orientation, that "azimuth"
is the new variable for making azimuth and window display to be times 90 degrees.
After that we change the new variable "azimuth" to the dirs array list.*/
//Direction display from the array list from 1 to 9 elements
val where = findViewById<TextView>(R.id.direction_text)
dirs = arrayListOf(
"North",
"North East",
"East",
"South East",
"South",
"South West",
"West",
"North West",
"North"
)
// We change the "azimuth" variable here, along with it's calculations.
var azimuth = degreeAzimuth + (windowOrientation * 90)
when (windowOrientation) {
//Portrait
Surface.ROTATION_0 -> {
if (azimuth > 360) {
azimuth -= 360
}
azimuthTxt.text = resources.getString(R.string.value_format, azimuth)
where.text = dirs[((azimuth + 22.5) / 45.0).toInt()]
}
//Landscape
Surface.ROTATION_90 -> {
if (azimuth > 360) {
azimuth -= 360
}
azimuthTxt.text = resources.getString(R.string.value_format, azimuth)
where.text = dirs[((azimuth + 22.5) / 45.0).toInt()]
}
//Reverse Portrait
Surface.ROTATION_180 -> {
if (azimuth > 360) {
azimuth -= 360
}
azimuthTxt.text = resources.getString(R.string.value_format, azimuth)
where.text = dirs[((azimuth + 22.5) / 45.0).toInt()]
}
//Reverse Landscape
Surface.ROTATION_270 -> {
if (azimuth > 360) {
azimuth -= 360
}
azimuthTxt.text = resources.getString(R.string.value_format, azimuth)
where.text = dirs[((azimuth + 22.5) / 45.0).toInt()]
}
}
currentDirection = dirs[((azimuth + 22.5) / 45.0).toInt()]
currentAzimuth = azimuth
val azimuthText = findViewById<TextView>(R.id.azimuthText)
azimuthText.text = resources.getString(R.string.value_format_2, currentAzimuth)
currentPitch = degreePitch
val pitchText = findViewById<TextView>(R.id.pitchText)
pitchText.text = resources.getString(R.string.value_format, currentPitch)
currentRoll = degreeRoll
val rollText = findViewById<TextView>(R.id.rollText)
rollText.text = resources.getString(R.string.value_format, currentRoll)
} catch (e: Exception) {
e.printStackTrace()
}
}
currentAzimuth、currentPith、currentRoll -它们正在重新转换,并将其用作最终结果
azimuthDegree -仅用于计算目的,以适应显示表面的每个方向
另外,由于Sensor.TYPE_ORIENTATION
在Java中已被弃用,因此建议的方式如下:
override fun onResume() {
super.onResume()
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
actionBar?.hide()
//Makes a register to the listener about the default sensor for the Azimuth gravity and magnetic field
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
SensorManager.SENSOR_DELAY_GAME)
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_GAME)
}
/**The sensor method initializes the type sensors and calculates the gravity and geomagnetic field**/
@SuppressLint("CutPasteId")
override fun onSensorChanged(event: SensorEvent?) {
//Portrait measurements - Correct by default
try {
val windowOrientation = windowManager.defaultDisplay.orientation
//Announcing the alpha for timing
val alpha = 0.97f
//The Sensor Event selects a type between Accelerometer and Magnetic Field
when (event!!.sensor.type) {
Sensor.TYPE_ACCELEROMETER -> {
mGravity[0] = alpha * mGravity[0] + (1 - alpha) * event.values[0]
mGravity[1] = alpha * mGravity[1] + (1 - alpha) * event.values[1]
mGravity[2] = alpha * mGravity[2] + (1 - alpha) * event.values[2]
}
Sensor.TYPE_MAGNETIC_FIELD -> {
mGeomagnetic[0] = alpha * mGeomagnetic[0] + (1 - alpha) * event.values[0]
mGeomagnetic[1] = alpha * mGeomagnetic[1] + (1 - alpha) * event.values[1]
mGeomagnetic[2] = alpha * mGeomagnetic[2] + (1 - alpha) * event.values[2]
}
}
val rotationMatrix = FloatArray(9)
val inclinationMatrix = FloatArray(9)
val success: Boolean = SensorManager.getRotationMatrix(rotationMatrix, inclinationMatrix, mGravity, mGeomagnetic)
if (success) {
//Azimuth
degreeAzimuth = event!!.values[0].toDouble()
val azimuthTxt = findViewById<TextView>(R.id.azimuthText)
//This makes sure that the calculations between Pitch and Roll, doesn't switch places when the orientation is turned between Portrait and Landscape by default
var degreeRoll: Double
var degreePitch: Double
if (windowOrientation == 1 || windowOrientation == 3) {
degreeRoll = event.values[1].toDouble()
degreePitch = event.values[2].toDouble()
} else {
degreeRoll = event.values[2].toDouble()
degreePitch = event.values[1].toDouble()
}
// We change the "azimuth" variable here, along with it's calculations.
var azimuth = degreeAzimuth + (windowOrientation * 90)
when (windowOrientation) {
//Portrait
Surface.ROTATION_0 -> {
if (azimuth > 360) {
azimuth -= 360
}
azimuthTxt.text = resources.getString(R.string.value_format, azimuth)
where.text = dirs[((azimuth + 22.5) / 45.0).toInt()]
}
//Landscape
Surface.ROTATION_90 -> {
if (azimuth > 360) {
azimuth -= 360
}
azimuthTxt.text = resources.getString(R.string.value_format, azimuth)
where.text = dirs[((azimuth + 22.5) / 45.0).toInt()]
}
//Reverse Portrait
Surface.ROTATION_180 -> {
if (azimuth > 360) {
azimuth -= 360
}
azimuthTxt.text = resources.getString(R.string.value_format, azimuth)
where.text = dirs[((azimuth + 22.5) / 45.0).toInt()]
}
//Reverse Landscape
Surface.ROTATION_270 -> {
if (azimuth > 360) {
azimuth -= 360
}
azimuthTxt.text = resources.getString(R.string.value_format, azimuth)
where.text = dirs[((azimuth + 22.5) / 45.0).toInt()]
}
}
currentAzimuth = azimuth
val azimuthText = findViewById<TextView>(R.id.azimuthText)
azimuthText.text = resources.getString(R.string.value_format_2, currentAzimuth)
currentPitch = degreePitch
val pitchText = findViewById<TextView>(R.id.pitchText)
pitchText.text = resources.getString(R.string.value_format, currentPitch)
currentRoll = degreeRoll
val rollText = findViewById<TextView>(R.id.rollText)
rollText.text = resources.getString(R.string.value_format, currentRoll)
}
}
然后在其中分配AddWatermark类时使用onImageSave()
方法,如下所示:
val originalBitmap = AddWatermark().addWatermark(
BitmapFactory.decodeFile(photoFile.absolutePath),
firstWatermarkText = "Azimuth:$dirs, $currentAzimuth",
secondWatermarkText = "Pitch: $currentPitch, Roll: $currentRoll",
AddWatermark.WatermarkOptions(
AddWatermark.Corner.TOP_LEFT,
textSizeToWidthRatio = 0.017f,
paddingToWidthRatio = 0.03f,
textColor = Color.parseColor("#FF0000"),
shadowColor = null,
typeface = null
)
)
viewFinder.bitmap.let { originalBitmap }
val outputStream = FileOutputStream(photoFile.toString())
originalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream)
outputStream.flush()
outputStream.close()
https://stackoverflow.com/questions/69359347
复制相似问题