2015-12-27 25 views
8

Chcę przyciąć obraz bez uzyskania wyjątku OutOfMemory.
oznacza to, że mam x, y, szerokość i wysokość przyciętego obrazu i chcę przyciąć oryginalny obraz bez wnoszenia go do pamięci.
Tak, wiem, że BitmapRegionDecoder jest dobrym pomysłem, ale może przycięty obraz byłby zbyt duży, by przywrócić go do pamięci. Przytnij obraz bez OutOfMemory - Android

W rzeczywistości nie chcę copped bitmapy, po prostu chcę zapisać przycięty obraz z pliku źródłowego do pliku docelowego.


EDIT: Chcę Zapisz przycięty obraz nie tylko pokazując go w ImageView chcę zapisać go w nowym pliku bez wymiarów tracących

Jest to przykład
enter image description here

w tej sytuacji rozdzielczość kadrowanego zdjęcia wynosi 20000x20000, a poniższy kod nie będzie działał przyczyną OOM:

BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false); 
BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inPreferredConfig = Bitmap.Config.RGB_565; 
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width/2 - 100, height/2 - 100, width/2 + 100, height/2 + 100), options); 
mImageView.setImageBitmap(bitmap); 

użycie inSampleSize w celu zmniejszenia oryginalnego rozmiaru obrazu jest dobre, ale wynik i zapis nie jest już 20000x20000.

Jak mogę wykadrować 25000x25000 i zapisać część obrazu o rozdzielczości 20000x20000 w pliku?

+0

dlaczego głosować w dół? podziel się swoją wiedzą –

+1

Stack Overflow jest przeznaczony do programowania pytań. Jakie jest Twoje pytanie? Jeśli twoje pytanie brzmi "jak to zrobić?", Proszę wyjaśnij, co wypróbowałeś i jakie problemy napotkasz. – CommonsWare

+0

tak! moje pytanie brzmi: jak mam to zrobić w Androidzie? wypróbowałem wszystkie odpowiedzi dotyczące powiązanych pytań w StackOverflow i każda odpowiedź była zależna od obiektu 'Bitmap', który jest zagrożeniem OOM. Nie wiem, która część mojego pytania jest nieprecyzyjna @CommonsWare –

Odpowiedz

1

Mówiąc najprościej, to wymaga dużo programowania niskiego poziomu i optymalizacje.

Jak widać, wiele odpowiedzi w tym regionie wskazuje na ogólne koncepcje kompresji bitmap, itp., Które rzeczywiście mają zastosowanie w większości problemów, ale nie dotyczą wyłącznie Ciebie.

Również BitmapRegionDecoder sugerowany w odpowiedziach nie będzie działał prawidłowo. Z pewnością uniemożliwia załadowanie całej bitmapy w pamięci RAM, ale co z przyciętym obrazem? po przycięciu obrazu daje gigantyczną bitmapę, która bez względu na wszystko, daje OOM.

Ponieważ twój problem, zgodnie z opisem, wymaga, aby Bitmapy były zapisywane lub odczytywane z dysku tak, jak są zapisywane lub odczytywane z pamięci; coś, co nazywa się BufferedBitmap (lub podobną), która wydajnie obsługuje wymaganą pamięć, zapisując małe kawałki mapy bitowej na dysku i wykorzystując je później, unikając w ten sposób OOM.

Każde inne rozwiązanie, które chce rozwiązać problem skalowania, wykonuje tylko połowę pracy. czemu? ponieważ sam przycięty obraz może być zbyt duży dla pamięci (jak powiedziałeś).

Jednak rozwiązanie problemu przez skalowanie nie jest takie złe, jeśli nie zależy Ci na jakości przyciętego obrazu w porównaniu z jakością, jaką zobaczył użytkownik podczas jej przycinania. to właśnie robią Zdjęcia Google, po prostu zmniejsza jakość przyciętego obrazu, bardzo proste!

Nie widziałem żadnych klas BufferedBitmap (ale jeśli są, byłoby świetnie). Z pewnością przydadzą się w rozwiązywaniu podobnych problemów.

Możesz sprawdzić aplikację do wysyłania wiadomości telegramów, która jest wyposażona w mechanizm do przycinania obrazu w wersji open source; Zgadujesz, że to prawda, obsługuje wszystkie podobne nieprzyjemne prace z dobrym starym C ... Dlatego możemy stwierdzić, że dobre globalne rozwiązanie (lub lepiej powiedziane, JEDEN Z CAŁKOWICIE ZASTOSOWANYCH ROZWIĄZAŃ) wydaje się być programowaniem niskopoziomowym do obsługi dysku i pamiętaj o sobie.

Wiem, że moja odpowiedź nie dała żadnego rozwiązania typu "wklej-wklej" do twojego problemu, ale przynajmniej mam nadzieję, że dała ci kilka pomysłów, przyjacielu.

+0

Tak, masz rację. Przetestowałem kilka narzędzi do edycji obrazu i zgadnij co? nic nie dało mi wyniku, którego chciałem. –

1

Czy sprawdziłeś BitmapRegionDecoder? Wyodrębni to prostokąt z oryginalnego obrazu.

BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false); 
BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inPreferredConfig = Bitmap.Config.RGB_565; 
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width/2 - 100, height/2 - 100, width/2 + 100, height/2 + 100), options); 
mImageView.setImageBitmap(bitmap); 

http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html

+0

problem polega na tym, że może prostokąt byłby zbyt duży i ta linia może wyrzucać OOM 'Bitmap bitmap = bitmapRegionDecoder.decodeRegion (...' –

1

Problem ten można rozwiązać stosując BitmapFactory. Do oznaczenia oryginalnego rozmiaru mapy bitowej bez wkładania go do pamięci, wykonaj odłogowania:

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inJustDecodeBounds = true; 
BitmapFactory.decodeResource(..., options); 

int originalImageWith = options.outWidth; 
int originalImageHeight = options.outHeight; 

teraz można używać options.inSampleSize

Jeśli ustawiona na wartość> 1, żąda dekoder do podpróbie oryginalny obraz, zwracający mniejszy obraz, aby zapisać pamięć . Rozmiar próbki to liczba pikseli w jednym z wymiarów , które odpowiadają pojedynczemu pikselowi w zdekodowanej bitmapie. Na przykład: inSampleSize == 4 zwraca obraz o szerokości 1/4 szerokości/wysokości oryginału i 1/16 liczby pikseli. Każda wartość < = 1 traktuje się same jak 1. Uwaga: dekoder używa ostateczną wartość na podstawie uprawnień 2, każda inna wartość zostanie zaokrąglona w dół do najbliższej potęgi 2.

Teraz nie jest to idealne rozwiązanie, ale można zrobić matematykę, aby znaleźć najbliżej 2 współczynnik, który można wykorzystać na options.inSampleSize, aby zaoszczędzić pamięć.

+0

no dude, moim celem było coś zupełnie innego.Zaktualizuję moje pytanie –

+1

@SepehrBehroozi don 't zaktualizuj pytanie, ale poproś o nowe, już odpowiedziałem na bieżące pytanie tak, jak było, –

+0

z szacunkiem, nie zrobiłeś :) Chcę zapisać przycięty obraz w pliku nie tylko go pokazując. użycie "inSampleSize" jest dobre dla obrazu widoku, ale dla zapisania go, ponieważ wymiary tracą –

0

BitmapRegionDecoder to dobry sposób na przycinanie dużych lub dużych obrazów, ale jest dostępny z interfejsu API 10 i nowszych.

Istnieje klasa o nazwie BitmapRegionDecoder, która może ci pomóc, ale jest dostępna z interfejsu API 10 i nowszych.

Jeśli nie można go używać:

Wiele formatów obrazu są kompresowane i dlatego wymagają jakiegoś wczytywania do pamięci.

Musisz przeczytać o najlepszym formacie obrazu, który pasuje do twoich potrzeb, a następnie przeczytać go sam, wykorzystując tylko taką pamięć, której potrzebujesz.

trochę łatwiejszym zadaniem byłoby zrobić to wszystko w JNI, więc nawet jeśli będziesz używać dużo pamięci, to przynajmniej twoja aplikacja nie dostanie się do OOM tak szybko, ponieważ nie będzie ona ograniczona do maksymalny rozmiar sterty nałożony na zwykłe aplikacje.

Oczywiście, ponieważ Android jest open source, możesz spróbować użyć BitmapRegionDecoder i używać go do dowolnego urządzenia.

referencyjny: Crop image without loading into memory

Albo można znaleźć jakiś inny sposób na poniżej, które mogą być pomocne dla Ciebie:

Bitmap/Canvas use and the NDK