【HarmonyOS NEXT】FAQ之媒体开发(图片处理)

1、通过PixelMap_CreatePixelMap创建的对象,内存在ArkTS侧和Native侧是否共享

A:通过PixelMap_CreatePixelMap创建的对象在ArkTS侧和Native侧会共享同一份内存。

2、如何设置图片的高斯模糊效果

A:使用图像效果模块的blur接口,详情请参考链接:图像效果

3、调用imageSource.createPixelMap()报错“Create PixelMap error”

A:该问题是sampleSize取值错误导致的,sampleSize表示缩略图采样大小,当前只能取1。可通过DecodingOptions.desiredSize指定输出大小。

4、图片压缩API的质量参数quality与图片原始大小、压缩后大小的关系

A:对于有损压缩图片格式,如jpeg格式,质量参数会影响压缩后的图片大小,对于无损压缩图片格式,如png格式,质量参数不会影响压缩后的图片大小

对于有损压缩图片格式,压缩后的图片大小不仅取决于图片原始大小、图片压缩质量,还与图片中内容有较大关系,因此当前系统不支持设置压缩后的图片大小,如果应用想要指定压缩后图片大小,可以根据压缩结果调整质量参数,或者将pixelmap scale到更小的尺寸后再压缩。

5、图片编解码支持的格式有哪些

A:解决如下

  • 图片解码

指将所支持格式的存档图片解码成统一的PixelMap,以便在应用或系统中进行图片显示或图片处理。当前支持的存档图片格式包括JPEG、PNG、GIF、RAW、WebP、BMP、SVG。

  • 图片编码

指将PixelMap编码成不同格式的存档图片(当前仅支持编码为JPEG、WebP 和 PNG 格式),用于后续处理,如保存、传输等。

6、如何将相册选择的图片生成PixelMap

A:如下两种方法

方法一:

  1. 创建图库选择器实例,调用select()接口拉起photoPicker界面进行图片选择。图片选择成功后,返回PhotoSelectResult结果集。

  2. 通过photoAccessHelper模块中的getAssets接口获取媒体文件对应文件的uri。

  3. 调用getThumbnail获取缩略图。

方法二:

  1. 创建图库选择器实例,调用select()接口拉起photoPicker界面进行图片选择。图片选择成功后,返回PhotoSelectResult结果集。

  2. 待界面从图库返回后,使用fs.openSync接口,通过uri打开这个文件得到fd。这里需要注意接口权限参数是fs.OpenMode.READ_ONLY。

  3. 通过image使用image.createImageSource接口创建图片源实例。

  4. 然后根据imageSource创建pixelMap。

7、如何对相册图片进行编辑裁剪

A:可以通过图片处理模块的pixelMap方法对图片进行编辑裁剪。

其中包括但不限于:

  • pixelMap.crop方法,可以根据输入的尺寸对图片进行裁剪。

  • pixelMap.opacity方法,可以通过设置透明比率对图片设置透明效果。

  • pixelMap.scale方法,可以根据输入的宽高对图片进行缩放。

  • pixelMap.rotate方法,可以根据输入的角度对图片进行旋转。

  • pixelMap.flip方法,可以根据输入的条件对图片进行翻转。

8、如何设置图片显示的分辨率

A:可以通过sourceSize属性设置图片分辨率,实例代码如下所示,原图尺寸为1280960,该示例将图片解码为4040。

9、如何保存本地图片到相册中

A:步骤如下

  1. 在模块级module.json5中申明权限

    "requestPermissions": [
    {
      "name":
      "ohos.permission.CAMERA", "usedScene":
      {
        "abilities": [
        "EntryAbility"       ],
        "when":
        "inuse"
      }
      ,
      "reason":
      "$string:CAMERA"
    }
    ,
    {
      "name":
      "ohos.permission.READ_IMAGEVIDEO", "usedScene":
      {
        "abilities": [
        "EntryAbility"       ],
        "when":
        "inuse"
      }
      ,
      "reason":
      "$string:CAMERA"
    }
    ,
    {
      "name":
      "ohos.permission.WRITE_IMAGEVIDEO", "usedScene":
      {
        "abilities": [
        "EntryAbility"       ],
        "when":
        "inuse"
      }
      ,
      "reason":
      "$string:CAMERA"
    }
    ]
  2. 获取权限

    import { abilityAccessCtrl, PermissionRequestResult, Permissions, bundleManager, common } from '@kit.AbilityKit';
    ​
    const TAG: string = '[Permission]';
    const PERMISSIONS: Array<Permissions> =
      ['ohos.permission.CAMERA', "ohos.permission.READ_IMAGEVIDEO", "ohos.permission.WRITE_IMAGEVIDEO"];
    const context = getContext(this) as common.UIAbilityContext;
    ​
    export default async function grantPermission(): Promise<boolean> {
      try {
        let bundleInfo: bundleManager.BundleInfo =
          await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
        let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
        let tokenId = appInfo.accessTokenId;
        let atManager = abilityAccessCtrl.createAtManager();
        let pems: Array<Permissions> = [];
        for (let i = 0; i < PERMISSIONS.length; i++) {
          let state = await atManager.checkAccessToken(tokenId, PERMISSIONS[i]);
          if (state !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
            pems.push(PERMISSIONS[i]);
          }
        }
        if (pems.length > 0) {
          let ctx = context
          let result: PermissionRequestResult = await atManager.requestPermissionsFromUser(ctx, pems);
          let grantStatus: Array<number> = result.authResults;
          let length: number = grantStatus.length;
          for (let i = 0; i < length; i++) {
            if (grantStatus[i] !== 0) {
              return false;
            }
          }
        }
        return true;
      } catch (error) {
        return false;
      }
    }
  3. 将本地图片的buffer传入如下存储函数中

    async savePicture(buffer: ArrayBuffer): Promise<void> { 
        let photoAccessHelper: PhotoAccessHelper.PhotoAccessHelper = PhotoAccessHelper.getPhotoAccessHelper(getContext(this) as common.UIAbilityContext);
        let options: PhotoAccessHelper.CreateOptions = {
          title: Date.now().toString()
        };
        let photoUri: string = await photoAccessHelper.createAsset(PhotoAccessHelper.PhotoType.IMAGE, 'jpg', options);
        console.info(photoUri)
        //createAsset的调用需要ohos.permission.READ_IMAGEVIDEO和ohos.permission.WRITE_IMAGEVIDEO的权限 
        let file: fs.File = fs.openSync(photoUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
        await fs.write(file.fd, buffer);
        fs.closeSync(file);
    }

10、如何读取相册中的图片

A:使用photoAccessHelper.PhotoSelectOptions接口

import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { image } from '@kit.ImageKit';
import { fileIo as fs } from '@kit.CoreFileKit';
​
@Entry
@Component
struct Index {
  @State getAlbum: string = '显示相册中的图片';
  @State pixel: image.PixelMap | undefined = undefined;
  @State albumPath: string = '';
  @State photoSize: number = 0;
​
  async getPictureFromAlbum() {
    // 拉起相册,选择图片
    let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
    PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
    PhotoSelectOptions.maxSelectNumber = 1;
    let photoPicker = new photoAccessHelper.PhotoViewPicker();
    let photoSelectResult: photoAccessHelper.PhotoSelectResult = await photoPicker.select(PhotoSelectOptions);
    this.albumPath = photoSelectResult.photoUris[0];
​
    // 读取图片为buffer
    const file = fs.openSync(this.albumPath, fs.OpenMode.READ_ONLY);
    this.photoSize = fs.statSync(file.fd).size;
    console.info('Photo Size: ' + this.photoSize);
    let buffer = new ArrayBuffer(this.photoSize);
    fs.readSync(file.fd, buffer);
    fs.closeSync(file);
​
    // 解码成PixelMap
    const imageSource = image.createImageSource(buffer);
    console.log('imageSource: ' + JSON.stringify(imageSource));
    this.pixel = await imageSource.createPixelMap({});
  }
​
  build() {
    Row() {
      Column() {
        Image(this.pixel)
          .width('100%')
          .aspectRatio(1)
        Button('显示照片')
          .onClick(() => {
            this.getPictureFromAlbum();
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

11、如何把ImageReceiver收到的视频帧数据保存到本地

A:如实例代码所示,在示例代码中保存接收到的前三帧数据,也可以通过业务需要调整。

let size: image.Size = {
  width: 640,
  height: 480
}
let receiver: image.ImageReceiver = image.createImageReceiver(size, image.ImageFormat.JPEG, 8);
receiver.on('imageArrival', () => {
  console.info("imageArrival callback");
  receiver.readNextImage((err: BusinessError, nextImage: image.Image) => {
    if (err || nextImage === undefined) {
      console.error("receiveImage -error:" + err + " nextImage:" + nextImage);
      return;
    }
    nextImage.getComponent(image.ComponentType.JPEG, (err: BusinessError, imgComponent: image.Component) => {
      if (err || imgComponent === undefined) {
        console.error("receiveImage--getComponent -error:" + err + " imgComponent:" + imgComponent);
        return;
      }
​
      if (imgComponent.byteBuffer as ArrayBuffer) {
        let sourceOptions: image.SourceOptions = {
          sourceDensity: 120,
          sourcePixelFormat: 8,
          sourceSize: {
            height: 1080,
            width: 1920
          },
        }
        let imageResource = image.createImageSource(imgComponent.byteBuffer, sourceOptions);
        let imagePackerApi = image.createImagePacker();
        let packOpts: image.PackingOption = { format: "image/jpeg", quality: 90 };
        const filePath: string = getContext().cacheDir + "/image.jpg";
        let file = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
        imagePackerApi.packToFile(imageResource, file.fd, packOpts).then(() => {
          console.error('pack success: ' + filePath);
        }).catch((error: BusinessError) => {
          console.error('Failed to pack the image. And the error is: ' + error);
        })
        imageResource.createPixelMap({}).then((res) => {
          this.imgUrl = res;
        });
      } else {
        return;
      }
      nextImage.release();
    });
  });
});

12、为什么获取到的yuv数据量比宽 * 高 * 1.5多

A:这是正常规格,多出来的为YUV尾部数据,不影响YUV数据的解析。

13、如何保存网络图片到相册

A:步骤如下

  1. 在模块级module.json5中申明权限

    "requestPermissions": [
    {
      "name":
      'ohos.permission.INTERNET', "usedScene":
      {
        "abilities": [
        "EntryAbility"       ],
        "when":
        "inuse"
      }
      ,
      "reason":
      "$string:reason"
    }
    ,
    {
      "name":
      "ohos.permission.WRITE_IMAGEVIDEO", "usedScene":
      {
        "abilities": [
        "EntryAbility"       ],
        "when":
        "inuse"
      }
      ,
      "reason":
      "$string:reason"
    }
    ,
    ]
  2. 创建如下页面

    import { http } from '@kit.NetworkKit' 
    import { BusinessError } from '@kit.BasicServicesKit';
    import { photoAccessHelper } from '@kit.MediaLibraryKit';
    import { fileIo } from '@kit.CoreFileKit';
    import { abilityAccessCtrl, PermissionRequestResult, Permissions, bundleManager, common } from '@kit.AbilityKit';
    ​
    @Entry
    @Component
    struct Index {
      private PERMISSIONS: Array<Permissions> = ['ohos.permission.INTERNET', "ohos.permission.WRITE_IMAGEVIDEO"];
      @State str:string = ''
    ​
      async grantPermission(): Promise<boolean> {
        try {
          let bundleInfo: bundleManager.BundleInfo =
            await bundleManager.getBundleInfoForSelf(
              bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION
            );
          let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
          let tokenId = appInfo.accessTokenId;
          let atManager = abilityAccessCtrl.createAtManager();
          let pems: Array<Permissions> = [];
          for (let i = 0; i < this.PERMISSIONS.length; i++) {
            let state = await atManager.checkAccessToken(tokenId, this.PERMISSIONS[i]);
            if (state !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
              pems.push(this.PERMISSIONS[i]);
            }
          }
          if (pems.length > 0) {
            let ctx = getContext(this) as common.UIAbilityContext
            let result: PermissionRequestResult = await atManager.requestPermissionsFromUser(ctx, pems);
            let grantStatus: Array<number> = result.authResults;
            let length: number = grantStatus.length;
            for (let i = 0; i < length; i++) {
              if (grantStatus[i] !== 0) {
                return false;
              }
            }
          }
          return true;
        } catch (error) {
          return false;
        }
      }
    ​
      loadImageWithUrl(url: string) {
        // 使用request下载图片并在回调函数中保存图片到相册 
        http.createHttp().request(url,
          {
            method: http.RequestMethod.GET,
            connectTimeout: 60000,
            readTimeout: 60000
          },
          async (error: BusinessError, data: http.HttpResponse) => {
            if (error) {
              console.error(`http reqeust failed with. Code: ${error.code}, message: ${error.message}`);
            } else {
              if (http.ResponseCode.OK === data.responseCode) {
                let imageBuffer: ArrayBuffer = data.result as ArrayBuffer;
                try {
                  // 获取相册路径 
                  const context = getContext(this);
                  let helper = photoAccessHelper.getPhotoAccessHelper(context);
                  let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg')
                  let file = await fileIo.open(uri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE)
                  // 写入文件 
                  await fileIo.write(file.fd, imageBuffer);
                  // 关闭文件 
                  await fileIo.close(file.fd);
                } catch (error) {
                  console.error("error is " + JSON.stringify(error))
                }
              } else {
                console.error("error occurred when image downloaded!")
              }
            }
          })
      }
    ​
      async aboutToAppear() {
        await this.grantPermission().then(async () => {
        }).catch((err: BusinessError) => {
          console.info(`grantPermission faild  ${JSON.stringify(err.code)}`);
        })
      }
    ​
      build() {
        Row() {
          Column() {
            TextInput({text:this.str, placeholder: '输入图片地址'})
              .onChange((value: string) => {
                this.str = value
              })
            Button('保存图片')
              .onClick(() => {
                this.loadImageWithUrl(this.str)
              })
          }
          .width('100%')
        }
        .height('100%')
      }
    }

14、通过OH_Pixelmap_CreatePixelMap创建的对象,内存是如何分配的

A:通过OH_Pixelmap_CreatePixelMap创建的对象在ArkTS侧和Native侧会共享同一份内存。

15、如何实现PixelMap和base64的相互转换

A:措施如下

  • PixelMap转base64:PixelMap转换成base64必须先使用imagePacker将pixelMap压缩后再进行base64转换。若要实现无损压缩,将PixelMap压缩成PNG,即调用imagePacker时,let packOpts: image.PackingOption = { format: 'image/png', quality: 100 };

  • base64转PixelMap:先将base64字符串解析成arraybuffer,然后利用这个arraybuffer构建新PixelMap,需要注意的是,使用decodeSync对base64字符串解码时,传入的base64字符串不能有'data:image/jpeg;base64,'这样的前缀。

16、如何将PixelMap压缩到指定大小以下

A:目前没有直接的接口支持将PixelMap压缩到指定大小以下,但可以通过循环压缩的方式实现,具体可参考如下方案实现压缩:

  1. 调用自定义compressedImage方法,传入要压缩图片的pixelMap和指定图片的压缩目标大小。

  2. 先判断设置图片质量参数quality为0时,packing能压缩到的图片最小字节大小是否满足指定的图片压缩大小。如果满足,则使用packing方式二分查找最接近指定图片压缩目标大小的quality来循环压缩图片。如果不满足,则使用scale对图片先进行缩放,采用while循环每次递减0.4倍缩放图片,再用packing(图片质量参数quality设置0)获取压缩图片大小,最终查找到最接近指定图片压缩目标大小的缩放倍数的图片压缩数据。

17、错误码62980096怎么处理

A:pixelFormat枚举目前是给ImageSource用的,所以NV21或者NV12格式的图片如果要创建PixelMap需要通过以下方式:

  1. 首先通过createImageSource创建ImageSource。

    此处需要设置 createimagesource的sourceOption参数,其中sourcePixelFormat参数的值8对应NV21格式,9对应NV12格式;sourceSize参数需要设置宽高(原始yuv图片的宽高数据),且width值不能为奇数。

  2. 然后通过ImageSource的createPixelMap接口创建PixelMap。

18、如何将C++侧接收的PixelMap转换成cv::mat格式

A:将ArkTS侧传到Native侧的PixelMap转换成cv::mat有两种方法:

  • 将PixelMap的arraybuffer转换成cv::mat。

  • 使用OH_PixelMap_AccessPixels获取PixelMap的内存地址,将这个内存地址中的数据转换为cv::mat。

上述两种方法都必须保证PixelMap的格式与opencv中mat的格式一致,否则会出现色彩的偏差。

19、image.createPixelMap中pixelFormat不生效

A:目前image.createPixelMap默认只能使用BGRA_8888格式处理数据,通过Promise返回结果。后续会提供新接口,可以支持指定输入流格式。

20、如何将PixelMap保存到相册

A:PixelMap使用imagePacker.packToFile()的方法将ImageSource图片源编码后直接打包进文件。

更多详情及参考代码查看如下:文档中心

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值