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

JohnSnowLabs / spark-nlp / 15252839065

26 May 2025 11:30AM CUT coverage: 52.115% (-0.6%) from 52.715%
15252839065

Pull #14585

github

web-flow
Merge 625e5c10f into 56512b006
Pull Request #14585: SparkNLP 1131 - Introducing Florance-2

0 of 199 new or added lines in 4 files covered. (0.0%)

50 existing lines in 33 files now uncovered.

9931 of 19056 relevant lines covered (52.11%)

0.52 hits per line

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

54.22
/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)
1✔
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

UNCOV
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
  def readImage(inputStream: InputStream): Option[BufferedImage] = {
77
    Try(ImageIO.read(inputStream)) match {
1✔
78
      case Success(bufferedImage) => Some(bufferedImage)
1✔
79
      case Failure(_) =>
80
        logger.warn(s"Error in ImageIOUtils.readImage while reading inputStream")
×
81
        None
×
82
    }
83
  }
84

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

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

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

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

118
  def bufferedImageToByte(img: BufferedImage): Array[Byte] = {
119

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

124
      val is_gray = img.getColorModel.getColorSpace.getType == ColorSpace.TYPE_GRAY
1✔
125

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

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

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

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

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

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

172
    (numberOfChannels, mode)
1✔
173
  }
174

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

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

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

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

202
  }
203

204
  def arrayToBufferedImage(pixelArray: Array[Array[Array[Int]]]): BufferedImage = {
205
    val height = pixelArray.length
×
206
    val width = pixelArray.head.length
×
207
    val image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
×
208

209
    for (y <- pixelArray.indices; x <- pixelArray(y).indices) {
×
210
      val rgb = pixelArray(y)(x) match {
×
211
        case Array(r, g, b) => new Color(r, g, b).getRGB
×
212
        case _ =>
213
          throw new IllegalArgumentException(
×
214
            "Each pixel must have exactly 3 color channels (RGB)")
215
      }
216
      image.setRGB(x, y, rgb)
×
217
    }
218
    image
219
  }
220
  def encodeImageBase64(image: Array[Byte]): String =
221
    java.util.Base64.getEncoder.encodeToString(image)
×
222

223
}
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