• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JohnSnowLabs / spark-nlp / 4951808959

pending completion
4951808959

Pull #13792

github

GitHub
Merge efe6b42df into ef7906c5e
Pull Request #13792: SPARKNLP-825 Adding multilabel param

7 of 7 new or added lines in 1 file covered. (100.0%)

8637 of 13128 relevant lines covered (65.79%)

0.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

51.35
/src/main/scala/com/johnsnowlabs/nlp/annotators/cv/util/io/ImageIOUtils.scala
1
/*
2
 * Copyright 2017-2022 John Snow Labs
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *    http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package com.johnsnowlabs.nlp.annotators.cv.util.io
18

19
import com.johnsnowlabs.nlp.ImageFields
20
import com.johnsnowlabs.nlp.util.io.ResourceHelper
21
import org.slf4j.LoggerFactory
22

23
import java.awt.color.ColorSpace
24
import java.awt.image.{BufferedImage, DataBufferByte, Raster}
25
import java.awt.{Color, Point}
26
import java.io.{File, InputStream}
27
import javax.imageio.ImageIO
28
import scala.util.{Failure, Success, Try}
29

30
private[johnsnowlabs] object ImageIOUtils {
31

32
  private val logger = LoggerFactory.getLogger("ImageIOUtils")
1✔
33

34
  /** (Scala-specific) OpenCV type mapping supported */
35
  val ocvTypes: Map[String, Int] =
36
    Map("CV_8U" -> 0, "CV_8UC1" -> 0, "CV_8UC3" -> 16, "CV_8UC4" -> 24)
1✔
37

38
  def loadImage(file: File): Option[BufferedImage] = {
39
    readImage(file)
×
40
  }
41

42
  def loadImage(inputStream: InputStream): Option[BufferedImage] = {
43
    readImage(inputStream)
×
44
  }
45

46
  def loadImage(path: String): Option[BufferedImage] = {
47
    val filePath = ResourceHelper.getFileFromPath(path)
1✔
48
    readImage(filePath)
1✔
49
  }
50

51
  def loadImageFromAnySource(path: String): Option[BufferedImage] = {
52

53
    val prefix = if (path.indexOf(":") == -1) "" else path.substring(0, path.indexOf(":"))
×
54

55
    prefix match {
56
      case "dbfs" =>
57
        loadImage(path.replace("dbfs:", "/dbfs/"))
×
58
      case "hdfs" =>
59
        val sourceStream = ResourceHelper.SourceStream(path)
×
60
        Some(ImageIO.read(sourceStream.pipe.head))
×
61
      case _ =>
62
        loadImage(path)
1✔
63
    }
64

65
  }
66

67
  def readImage(file: File): Option[BufferedImage] = {
68
    Try(ImageIO.read(file)) match {
1✔
69
      case Success(bufferedImage) => Some(bufferedImage)
1✔
70
      case Failure(_) => {
71
        logger.warn(s"Error in ImageIOUtils.readImage while reading file: ${file.getPath}")
×
72
        None
×
73
      }
74
    }
75
  }
76

77
  def readImage(inputStream: InputStream): Option[BufferedImage] = {
78
    Try(ImageIO.read(inputStream)) match {
×
79
      case Success(bufferedImage) => Some(bufferedImage)
×
80
      case Failure(_) => {
81
        logger.warn(s"Error in ImageIOUtils.readImage while reading inputStream")
×
82
        None
×
83
      }
84
    }
85
  }
86

87
  def loadImages(imagesPath: String): Array[File] = {
88
    loadImagesFromDirectory(imagesPath) match {
×
89
      case Success(files) => files
90
      case Failure(_) =>
91
        val singleImagePath = ResourceHelper.getFileFromPath(imagesPath)
×
92
        Array(singleImagePath)
×
93
    }
94
  }
95

96
  private def loadImagesFromDirectory(path: String): Try[Array[File]] = {
97
    Try {
×
98
      ResourceHelper.listLocalFiles(path).toArray
×
99
    }
100
  }
101

102
  def convertChannelsToType(channels: Int): Int = channels match {
103
    case 1 => BufferedImage.TYPE_BYTE_GRAY
×
104
    case 3 => BufferedImage.TYPE_3BYTE_BGR
×
105
    case 4 => BufferedImage.TYPE_4BYTE_ABGR
×
106
    case c =>
107
      throw new UnsupportedOperationException(
×
108
        "Image resize: number of output  " +
109
          s"channels must be 1, 3, or 4, got $c.")
110
  }
111

112
  def byteToBufferedImage(bytes: Array[Byte], w: Int, h: Int, nChannels: Int): BufferedImage = {
113
    val img = new BufferedImage(w, h, convertChannelsToType(nChannels))
×
114
    img.setData(
×
115
      Raster
116
        .createRaster(img.getSampleModel, new DataBufferByte(bytes, bytes.length), new Point()))
×
117
    img
118
  }
119

120
  def bufferedImageToByte(img: BufferedImage): Array[Byte] = {
121

122
    if (img == null) {
1✔
123
      Array.empty[Byte]
×
124
    } else {
1✔
125

126
      val is_gray = img.getColorModel.getColorSpace.getType == ColorSpace.TYPE_GRAY
1✔
127

128
      val height = img.getHeight
1✔
129
      val width = img.getWidth
1✔
130
      val (nChannels, _) = getChannelsAndMode(img)
1✔
131

132
      assert(height * width * nChannels < 1e9, "image is too large")
1✔
133
      val decoded = Array.ofDim[Byte](height * width * nChannels)
1✔
134

135
      // grayscale images in Java require special handling to get the correct intensity
136
      if (is_gray) {
×
137
        var offset = 0
×
138
        val raster = img.getRaster
×
139
        for (h <- 0 until height) {
×
140
          for (w <- 0 until width) {
×
141
            decoded(offset) = raster.getSample(w, h, 0).toByte
×
142
            offset += 1
×
143
          }
144
        }
145
      } else {
1✔
146
        var offset = 0
1✔
147
        for (h <- 0 until height) {
1✔
148
          for (w <- 0 until width) {
1✔
149
            val color = new Color(img.getRGB(w, h))
1✔
150

151
            decoded(offset) = color.getBlue.toByte
1✔
152
            decoded(offset + 1) = color.getGreen.toByte
1✔
153
            decoded(offset + 2) = color.getRed.toByte
1✔
154
            if (nChannels == 4) {
1✔
155
              decoded(offset + 3) = color.getAlpha.toByte
×
156
            }
157
            offset += nChannels
1✔
158
          }
159
        }
160
      }
161
      decoded
162
    }
163
  }
164

165
  private def getChannelsAndMode(bufferedImage: BufferedImage): (Int, Int) = {
166
    val is_gray = bufferedImage.getColorModel.getColorSpace.getType == ColorSpace.TYPE_GRAY
1✔
167
    val has_alpha = bufferedImage.getColorModel.hasAlpha
1✔
168

169
    val (numberOfChannels, mode) =
1✔
170
      if (is_gray) (1, ocvTypes.getOrElse("CV_8UC1", -1))
171
      else if (has_alpha) (4, ocvTypes.getOrElse("CV_8UC4", -1))
172
      else (3, ocvTypes.getOrElse("CV_8UC3", -1))
173

174
    (numberOfChannels, mode)
1✔
175
  }
176

177
  def imagePathToImageFields(imagePath: String): Option[ImageFields] = {
178
    val bufferedImage = loadImageFromAnySource(imagePath)
1✔
179
    bufferedImageToImageFields(bufferedImage, imagePath)
1✔
180
  }
181

182
  def imageFileToImageFields(file: File): Option[ImageFields] = {
183
    val bufferedImage = loadImage(file)
×
184
    bufferedImageToImageFields(bufferedImage, file.getPath)
×
185
  }
186

187
  def bufferedImageToImageFields(
188
      bufferedImage: Option[BufferedImage],
189
      origin: String): Option[ImageFields] = {
190
    if (bufferedImage.isDefined) {
1✔
191
      val (nChannels, mode) = getChannelsAndMode(bufferedImage.get)
1✔
192
      val data = bufferedImageToByte(bufferedImage.get)
1✔
193

194
      Some(
1✔
195
        ImageFields(
1✔
196
          origin,
197
          bufferedImage.get.getHeight,
1✔
198
          bufferedImage.get.getWidth,
1✔
199
          nChannels,
200
          mode,
201
          data))
202
    } else None
×
203

204
  }
205

206
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc