首页 [Android项目-Gallery3D][media-MediaClustering.java]源码

[Android项目-Gallery3D][media-MediaClustering.java]源码

举报
开通vip

[Android项目-Gallery3D][media-MediaClustering.java]源码[Android项目-Gallery3D][media-MediaClustering.java]源码 package com.cooliris.media; import java.util.ArrayList; import android.text.format.DateFormat; import android.text.format.DateUtils; import android.content.Context; import android.content.res.Resources; ...

[Android项目-Gallery3D][media-MediaClustering.java]源码
[Android项目-Gallery3D][media-MediaClustering.java]源码 package com.cooliris.media; import java.util.ArrayList; import android.text.format.DateFormat; import android.text.format.DateUtils; import android.content.Context; import android.content.res.Resources; /** * Implementation of an agglomerative based clustering where all items within a * certain time cutoff are grouped into the same cluster. Small adjacent * clusters are merged and large individual clusters are considered for * splitting. * * TODO: Limitation: Can deal with items not being added incrementally to the * end of the current date range but effectively assumes this is the case for * efficient performance. */ public final class MediaClustering { // If 2 items are greater than 25 miles apart, they will be in different // clusters. private static final int GEOGRAPHIC_DISTANCE_CUTOFF_IN_MILES = 20; // Do not want to split based on anything under 1 min. private static final long MIN_CLUSTER_SPLIT_TIME_IN_MS = 60000L; // Disregard a cluster split time of anything over 2 hours. private static final long MAX_CLUSTER_SPLIT_TIME_IN_MS = 7200000L; // Try and get around 9 clusters (best-effort for the common case). private static final int NUM_CLUSTERS_TARGETED = 9; // Try and merge 2 clusters if they are both smaller than min cluster size. // The min cluster size can range from 8 to 15. private static final int MIN_MIN_CLUSTER_SIZE = 8; private static final int MAX_MIN_CLUSTER_SIZE = 15; // Try and split a cluster if it is bigger than max cluster size. // The max cluster size can range from 20 to 50. private static final int MIN_MAX_CLUSTER_SIZE = 20; private static final int MAX_MAX_CLUSTER_SIZE = 50; // Initially put 2 items in the same cluster as long as they are within // 3 cluster frequencies of each other. private static int CLUSTER_SPLIT_MULTIPLIER = 3; // The minimum change factor in the time between items to consider a // partition. // Example: (Item 3 - Item 2) / (Item 2 - Item 1). private static final int MIN_PARTITION_CHANGE_FACTOR = 2; // Make the cluster split time of a large cluster half that of a regular // cluster. private static final int PARTITION_CLUSTER_SPLIT_TIME_FACTOR = 2; private ArrayList mClusters; private Cluster mCurrCluster; private boolean mIsPicassaAlbum = false; private long mClusterSplitTime = (MIN_CLUSTER_SPLIT_TIME_IN_MS + MAX_CLUSTER_SPLIT_TIME_IN_MS) / 2; private long mLargeClusterSplitTime = mClusterSplitTime / PARTITION_CLUSTER_SPLIT_TIME_FACTOR; private int mMinClusterSize = (MIN_MIN_CLUSTER_SIZE + MAX_MIN_CLUSTER_SIZE) / 2; private int mMaxClusterSize = (MIN_MAX_CLUSTER_SIZE + MAX_MAX_CLUSTER_SIZE) / 2; MediaClustering(boolean isPicassaAlbum) { mClusters = new ArrayList(); mIsPicassaAlbum = isPicassaAlbum; mCurrCluster = new Cluster(mIsPicassaAlbum); } public void clear() { int numClusters = mClusters.size(); for (int i = 0; i < numClusters; i++) { Cluster cluster = mClusters.get(i); cluster.clear(); } if (mCurrCluster != null) { mCurrCluster.clear(); } } public void setTimeRange(long timeRange, int numItems) { if (numItems != 0) { int meanItemsPerCluster = numItems / NUM_CLUSTERS_TARGETED; // Heuristic to get min and max cluster size - half and double the // desired items per cluster. mMinClusterSize = meanItemsPerCluster / 2; mMaxClusterSize = meanItemsPerCluster * 2; mClusterSplitTime = timeRange / numItems * CLUSTER_SPLIT_MULTIPLIER; } mClusterSplitTime = Shared.clamp(mClusterSplitTime, MIN_CLUSTER_SPLIT_TIME_IN_MS, MAX_CLUSTER_SPLIT_TIME_IN_MS); mLargeClusterSplitTime = mClusterSplitTime / PARTITION_CLUSTER_SPLIT_TIME_FACTOR; mMinClusterSize = Shared.clamp(mMinClusterSize, MIN_MIN_CLUSTER_SIZE, MAX_MIN_CLUSTER_SIZE); mMaxClusterSize = Shared.clamp(mMaxClusterSize, MIN_MAX_CLUSTER_SIZE, MAX_MAX_CLUSTER_SIZE); } public void addItemForClustering(MediaItem mediaItem) { compute(mediaItem, false); } public void removeItemFromClustering(MediaItem mediaItem) { // Find the cluster that contains this item. if (mCurrCluster.removeItem(mediaItem)) { return; } int numClusters = mClusters.size(); for (int i = 0; i < numClusters; i++) { Cluster cluster = mClusters.get(i); if (cluster.removeItem(mediaItem)) { if (cluster.mNumItemsLoaded == 0) { mClusters.remove(cluster); } return; } } } public void compute(MediaItem currentItem, boolean processAllItems) { if (currentItem != null) { int numClusters = mClusters.size(); int numCurrClusterItems = mCurrCluster.mNumItemsLoaded; boolean geographicallySeparateItem = false; boolean itemAddedToCurrentCluster = false; // Determine if this item should go in the current cluster or be the // start of a new cluster. if (numCurrClusterItems == 0) { mCurrCluster.addItem(currentItem); } else { MediaItem prevItem = mCurrCluster.getLastItem(); if (isGeographicallySeparated(prevItem, currentItem)) { mClusters.add(mCurrCluster); geographicallySeparateItem = true; } else if (numCurrClusterItems > mMaxClusterSize) { splitAndAddCurrentCluster(); } else if (timeDistance(prevItem, currentItem) < mClusterSplitTime) { mCurrCluster.addItem(currentItem); itemAddedToCurrentCluster = true; } else if (numClusters > 0 && numCurrClusterItems < mMinClusterSize && !mCurrCluster.mGeographicallySeparatedFromPrevCluster) { mergeAndAddCurrentCluster(); } else { mClusters.add(mCurrCluster); } // Creating a new cluster and adding the current item to it. if (!itemAddedToCurrentCluster) { mCurrCluster = new Cluster(mIsPicassaAlbum); if (geographicallySeparateItem) { mCurrCluster.mGeographicallySeparatedFromPrevCluster = true; } mCurrCluster.addItem(currentItem); } } } if (processAllItems && mCurrCluster.mNumItemsLoaded > 0) { int numClusters = mClusters.size(); int numCurrClusterItems = mCurrCluster.mNumItemsLoaded; // The last cluster may potentially be too big or too small. if (numCurrClusterItems > mMaxClusterSize) { splitAndAddCurrentCluster(); } else if (numClusters > 0 && numCurrClusterItems < mMinClusterSize && !mCurrCluster.mGeographicallySeparatedFromPrevCluster) { mergeAndAddCurrentCluster(); } else { mClusters.add(mCurrCluster); } mCurrCluster = new Cluster(mIsPicassaAlbum); } } private void splitAndAddCurrentCluster() { ArrayList currClusterItems = mCurrCluster.getItems(); int numCurrClusterItems = mCurrCluster.mNumItemsLoaded; int secondPartitionStartIndex = getPartitionIndexForCurrentCluster(); if (secondPartitionStartIndex != -1) { Cluster partitionedCluster = new Cluster(mIsPicassaAlbum); for (int j = 0; j < secondPartitionStartIndex; j++) { partitionedCluster.addItem(currClusterItems.get(j)); } mClusters.add(partitionedCluster); partitionedCluster = new Cluster(mIsPicassaAlbum); for (int j = secondPartitionStartIndex; j < numCurrClusterItems; j++) { partitionedCluster.addItem(currClusterItems.get(j)); } mClusters.add(partitionedCluster); } else { mClusters.add(mCurrCluster); } } private int getPartitionIndexForCurrentCluster() { int partitionIndex = -1; float largestChange = MIN_PARTITION_CHANGE_FACTOR; ArrayList currClusterItems = mCurrCluster.getItems(); int numCurrClusterItems = mCurrCluster.mNumItemsLoaded; int minClusterSize = mMinClusterSize; // Could be slightly more efficient here but this code seems cleaner. if (numCurrClusterItems > minClusterSize + 1) { for (int i = minClusterSize; i < numCurrClusterItems - minClusterSize; i++) { MediaItem prevItem = currClusterItems.get(i - 1); MediaItem currItem = currClusterItems.get(i); MediaItem nextItem = currClusterItems.get(i + 1); if (prevItem.isDateTakenValid() && currItem.isDateModifiedValid() && nextItem.isDateModifiedValid()) { long diff1 = Math.abs(nextItem.mDateTakenInMs - currItem.mDateTakenInMs); long diff2 = Math.abs(currItem.mDateTakenInMs - prevItem.mDateTakenInMs); float change = Math.max(diff1 / (diff2 + 0.01f), diff2 / (diff1 + 0.01f)); if (change > largestChange) { if (timeDistance(currItem, prevItem) > mLargeClusterSplitTime) { partitionIndex = i; largestChange = change; } else if (timeDistance(nextItem, currItem) > mLargeClusterSplitTime) { partitionIndex = i + 1; largestChange = change; } } } } } return partitionIndex; } private void mergeAndAddCurrentCluster() { int numClusters = mClusters.size(); Cluster prevCluster = mClusters.get(numClusters - 1); ArrayList currClusterItems = mCurrCluster.getItems(); int numCurrClusterItems = mCurrCluster.mNumItemsLoaded; if (prevCluster.mNumItemsLoaded < mMinClusterSize) { for (int i = 0; i < numCurrClusterItems; i++) { prevCluster.addItem(currClusterItems.get(i)); } mClusters.set(numClusters - 1, prevCluster); } else { mClusters.add(mCurrCluster); } } public synchronized ArrayList getClusters() { int numCurrClusterItems = mCurrCluster.mNumItemsLoaded; if (numCurrClusterItems == 0) { return mClusters; } ArrayList mergedClusters = new ArrayList(); mergedClusters.addAll(mClusters); if (numCurrClusterItems > 0) { mergedClusters.add(mCurrCluster); } return mergedClusters; } public ArrayList getClustersForDisplay() { return mClusters; } public static final class Cluster extends MediaSet { private boolean mGeographicallySeparatedFromPrevCluster = false; private boolean mClusterChanged = false; private boolean mIsPicassaAlbum = false; private static final String MMDDYY_FORMAT = "MMddyy"; public Cluster(boolean isPicassaAlbum) { mIsPicassaAlbum = isPicassaAlbum; } public void generateCaption(Context context) { if (mClusterChanged) { Resources resources = context.getResources(); long minTimestamp = -1L; long maxTimestamp = -1L; if (areTimestampsAvailable()) { minTimestamp = mMinTimestamp; maxTimestamp = mMaxTimestamp; } else if (areAddedTimestampsAvailable()) { minTimestamp = mMinAddedTimestamp; maxTimestamp = mMaxAddedTimestamp; } if (minTimestamp != -1L) { if (mIsPicassaAlbum) { minTimestamp -= Gallery.CURRENT_TIME_ZONE.getOffset(minTimestamp); maxTimestamp -= Gallery.CURRENT_TIME_ZONE.getOffset(maxTimestamp); } String minDay = DateFormat.format(MMDDYY_FORMAT, minTimestamp).toString(); String maxDay = DateFormat.format(MMDDYY_FORMAT, maxTimestamp).toString(); if (minDay.substring(4).equals(maxDay.substring(4))) { // The items are from the same year - show at least as // much granularity as abbrev_all allows. mName = DateUtils.formatDateRange(context, minTimestamp, maxTimestamp, DateUtils.FORMAT_ABBREV_ALL); // Get a more granular date range string if the min and // max timestamp are on the same day and from the // current year. if (minDay.equals(maxDay)) { int flags = DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_SHOW_DATE; // Contains the year only if the date does not // correspond to the current year. String dateRangeWithOptionalYear = DateUtils.formatDateTime(context, minTimestamp, flags); String dateRangeWithYear = DateUtils.formatDateTime(context, minTimestamp, flags | DateUtils.FORMAT_SHOW_YEAR); if (!dateRangeWithOptionalYear.equals(dateRangeWithYear)) { // This means both dates are from the same year // - show the time. // Not enough room to display the time range. // Pick the mid-point. long midTimestamp = (minTimestamp + maxTimestamp) / 2; mName = DateUtils.formatDateRange(context, midTimestamp, midTimestamp, DateUtils.FORMAT_SHOW_TIME | flags); } } } else { // The items are not from the same year - only show // month and year. int flags = DateUtils.FORMAT_NO_MONTH_DAY | DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_SHOW_DATE; mName = DateUtils.formatDateRange(context, minTimestamp, maxTimestamp, flags); } } else { mName = resources.getString(R.string.date_unknown); } updateNumExpectedItems(); generateTitle(false); mClusterChanged = false; } } public void addItem(MediaItem item) { super.addItem(item); mClusterChanged = true; } public boolean removeItem(MediaItem item) { if (super.removeItem(item)) { mClusterChanged = true; return true; } return false; } public MediaItem getLastItem() { final ArrayList items = super.getItems(); if (items == null || mNumItemsLoaded == 0) { return null; } else { return items.get(mNumItemsLoaded - 1); } } } // Returns the time interval between the two items in milliseconds. public static long timeDistance(MediaItem a, MediaItem b) { if (a == null || b == null) { return 0; } return Math.abs(a.mDateTakenInMs - b.mDateTakenInMs); } // Returns true if a, b are sufficiently geographically separated. private static boolean isGeographicallySeparated(MediaItem a, MediaItem b) { // If a or b are null, a or b have the default latitude, longitude // values or are close enough, return false. if (a != null && b != null && a.isLatLongValid() && b.isLatLongValid()) { int distance = (int) (LocationMediaFilter.toMile(LocationMediaFilter.distanceBetween(a.mLatitude, a.mLongitude, b.mLatitude, b.mLongitude)) + 0.5); if (distance > GEOGRAPHIC_DISTANCE_CUTOFF_IN_MILES) { return true; } } return false; } }
本文档为【[Android项目-Gallery3D][media-MediaClustering&#46;java]源码】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_977556
暂无简介~
格式:doc
大小:46KB
软件:Word
页数:16
分类:生活休闲
上传时间:2017-10-22
浏览量:7