メモリにロードせずにSDカードに保存された画像を回転させることは可能だろうかと思います。
その理由は、私が探しているのは有名なOutOfMemoryErrorです。大きな画像をダウンサンプリングすることで回避できることはわかっていますが、実際にはその画像のサイズを縮小したくはありません。元の画像を90度回転させたいのです。
それについての提案は大歓迎です:)
メモリにロードせずにSDカードに保存された画像を回転させることは可能だろうかと思います。
その理由は、私が探しているのは有名なOutOfMemoryErrorです。大きな画像をダウンサンプリングすることで回避できることはわかっていますが、実際にはその画像のサイズを縮小したくはありません。元の画像を90度回転させたいのです。
それについての提案は大歓迎です:)
90度の回転では、ビットマップを処理するように正確に設計されたRenderScriptを実際に採用しており、デフォルトよりも予想外に高速ですBitmap.createBitmap()
。処理中のビットマップはJavaヒープに格納されないため、にプッシュされませんOutOfMemoryError
。
プロジェクトで数行のRenderScriptサポートを設定した後、使用するRenderScriptアルゴリズムは次のとおりです。
1)app\src\main\rs\rotator.rs
以下の内容でRenderScriptファイルを作成します。
#pragma version(1)
#pragma rs java_package_name(ua.kulku.rs)
rs_allocation inImage;
int inWidth;
int inHeight;
uchar4 __attribute__ ((kernel)) rotate_90_clockwise (uchar4 in, uint32_t x, uint32_t y) {
uint32_t inX = inWidth - 1 - y;
uint32_t inY = x;
const uchar4 *out = rsGetElementAt(inImage, inX, inY);
return *out;
}
uchar4 __attribute__ ((kernel)) rotate_270_clockwise (uchar4 in, uint32_t x, uint32_t y) {
uint32_t inX = y;
uint32_t inY = inHeight - 1 - x;
const uchar4 *out = rsGetElementAt(inImage, inX, inY);
return *out;
}
ua.kulku.rs
これは、RSJavaインターフェースの自動生成に選択するパッケージ名であることに注意してください。
2)Javaコードで参照します。
import ua.kulku.rs.ScriptC_rotator;
public Bitmap rotate(Bitmap bitmap) {
RenderScript rs = RenderScript.create(mContext);
ScriptC_rotator script = new ScriptC_rotator(rs);
script.set_inWidth(bitmap.getWidth());
script.set_inHeight(bitmap.getHeight());
Allocation sourceAllocation = Allocation.createFromBitmap(rs, bitmap,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
bitmap.recycle();
script.set_inImage(sourceAllocation);
int targetHeight = bitmap.getWidth();
int targetWidth = bitmap.getHeight();
Bitmap.Config config = bitmap.getConfig();
Bitmap target = Bitmap.createBitmap(targetWidth, targetHeight, config);
final Allocation targetAllocation = Allocation.createFromBitmap(rs, target,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
script.forEach_rotate_90_clockwise(targetAllocation, targetAllocation);
targetAllocation.copyTo(target);
rs.destroy();
return target;
}
180度の回転は、実際には画像のピクセル配列の反転であるため、シーケンシャル配列アイテムへのアクセスを利用しているため、180度の回転では、NDKソリューションがRenderScriptを上回りました。これらの比較で使用したNDKアルゴリズムは、 https://github.com/AndroidDeveloperLB/AndroidJniBitmapOperationsからのものです。処理中のビットマップもJavaヒープに格納されないため、OutOfMemoryError
。
統計バーは、Samsung S4(Android 5.0)で13MPの写真に対してミリ秒単位で取得したものを示しています。
を使用して画像をデコードデコードする必要がありますBitmap
。あなたはそれを行う方法についてグーグルによって提示された大きな画像の読み込みに従う必要があります..それは私を大いに助けました、あなたはRAM使用量の大きな違いに気付くでしょう..
画像を回転させるだけの場合は、このコードを使用して更新してください
Matrix matrix = new Matrix();
matrix.setRotate(90);
result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, false);
画像の向き(たとえば、撮影時の写真の向き)を設定するだけでよい場合は、次を使用できます。
ExifInterface exif = new ExifInterface(filePath);
属性ExifInterface.TAG_ORIENTATION
を使用して、これがお役に立てば幸いです
注:この回答は、riwnodennykの回答の実装を少し簡単にすることで実際に拡張されているだけです。これはすべてrenderscriptであり、NDKには関係ありません。
すべての一般的な回転とオリエンテーションの取得をカバーしています。
回転を取得するには、ExifInterfaceを使用する必要があります。SDKバージョンに問題があるため、サポートライブラリにあるものを使用してください。この情報は(デコードされたビットマップではなく)ファイルに埋め込まれているため、JPEGおよびRAW(および同様の)ファイルで機能します。
build.gradleに追加
implementation "com.android.support:exifinterface:28.0.0"
ファイルへのハンドルがある場合はこれを使用します
import android.renderscript.Allocation;
import android.renderscript.RenderScript;
import your.application.package.rs.ScriptC_rotator;
...
public static Bitmap getCorrectlyRotatedBitmap(@NonNull Context context, @NonNull File imageFile) throws IOException {
ExifInterface ei = new ExifInterface(imageFile.getPath());
Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getPath());
int neededRotationClockwise = ei.getRotationDegrees() % 360;
return rotateClockwise(context, bitmap, neededRotationClockwise);
}
ビットマップローテーション自体の場合
public static Bitmap rotateClockwise(@NonNull Context context, @NonNull Bitmap bitmap, int degrees) {
Log.i(TAG, "rotate bitmap degrees: " + degrees);
if (degrees == 0F) return bitmap;
RenderScript rs = RenderScript.create(context);
ScriptC_rotator script = new ScriptC_rotator(rs);
script.set_inWidth(bitmap.getWidth());
script.set_inHeight(bitmap.getHeight());
Allocation sourceAllocation = Allocation.createFromBitmap(rs, bitmap,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
bitmap.recycle();
script.set_inImage(sourceAllocation);
Bitmap.Config config = bitmap.getConfig();
switch (degrees) {
case 90: {
Bitmap target = Bitmap.createBitmap(bitmap.getHeight(), bitmap.getWidth(), config);
final Allocation targetAllocation = Allocation.createFromBitmap(rs, target,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
script.forEach_rotate_90_clockwise(targetAllocation, targetAllocation);
targetAllocation.copyTo(target);
rs.destroy();
return target;
}
case 180: {
Bitmap target = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), config);
final Allocation targetAllocation = Allocation.createFromBitmap(rs, target,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
script.forEach_rotate_180(targetAllocation, targetAllocation);
targetAllocation.copyTo(target);
rs.destroy();
return target;
}
case 270: {
Bitmap target = Bitmap.createBitmap(bitmap.getHeight(), bitmap.getWidth(), config);
final Allocation targetAllocation = Allocation.createFromBitmap(rs, target,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
script.forEach_rotate_270_clockwise(targetAllocation, targetAllocation);
targetAllocation.copyTo(target);
rs.destroy();
return target;
}
default:
throw new IllegalArgumentException("rotateClockwise() only supports 90 degree increments");
}
}
そして、renderscript、Create file in src/main/rs/rotator.rs
、これはRenderscriptが使用されるデフォルトの場所ですが、ディレクトリを自分で作成する必要があります。
必要に応じて変更your.application.package.rs
します
#pragma version(1)
#pragma rs java_package_name(your.application.package.rs)
rs_allocation inImage;
int inWidth;
int inHeight;
uchar4 __attribute__ ((kernel)) rotate_270_clockwise (uchar4 in, uint32_t x, uint32_t y) {
uint32_t inX = inWidth - 1 - y;
uint32_t inY = x;
const uchar4 *out = rsGetElementAt(inImage, inX, inY);
return *out;
}
uchar4 __attribute__ ((kernel)) rotate_90_clockwise (uchar4 in, uint32_t x, uint32_t y) {
uint32_t inX = y;
uint32_t inY = inHeight - 1 - x;
const uchar4 *out = rsGetElementAt(inImage, inX, inY);
return *out;
}
uchar4 __attribute__ ((kernel)) rotate_180 (uchar4 in, uint32_t x, uint32_t y) {
uint32_t inX = inWidth - 1 - x;
uint32_t inY = inHeight - 1 - y;
const uchar4 *out = rsGetElementAt(inImage, inX, inY);
return *out;
}
uchar4 __attribute__ ((kernel)) flip_vertical (uchar4 in, uint32_t x, uint32_t y) {
uint32_t inX = x;
uint32_t inY = inHeight - 1 - y;
const uchar4 *out = rsGetElementAt(inImage, inX, inY);
return *out;
}
uchar4 __attribute__ ((kernel)) flip_horizontal (uchar4 in, uint32_t x, uint32_t y) {
uint32_t inX = inWidth - 1 - x;
uint32_t inY = y;
const uchar4 *out = rsGetElementAt(inImage, inX, inY);
return *out;
}
私はここで非常に遅いがメモリに優しいソリューションを作成しました。
もっと良い方法があると確信していて、それらを知りたいです
さまざまなフォーマットを処理する必要がある場合、それは苦痛になります。さまざまな形式を理解し、おそらくストリームを介してそれらを読み取り/書き込み/変換できる必要があります。通常のPCでは、非常に大きな画像サポートを備えたImageMagickを見てみたいと思います。私はAndroidポートを検索し、これを思いついた。試してみる価値があるかもしれません。ただし、未完成のように見えるので、フォーマットのカバレッジを向上させるために、おそらくいくつかの作業を行う必要があります。
ビットマップの操作中にプロセスヒープにデータを保存しないサードパーティのライブラリを使用することをお勧めします。私の場合、プロジェクトで他の目的にすでに使用していたffmpegを使用しました。